Libraries in C/C++


Introduction

In accordance with Wikipedia:

Library in programming is a compilation of subprograms or objects used for software development.

In general case any file can be called as a library that contains additional supportive functions, macros and other elements of programming code, which can be connected to the main program at the stage of its conversion to machine code.

Libraries can be divided conditionally on “standard” and “users”, created directly by developer or being installed additionally.

Creating and using our own created libraries we pursue two main targets:

  • Creation of universal set of functions and other elements of program code, suitable for being applied in different software.
  • Division of the code to different modules, each of them can be considered and tested separately, which simplify development, debugging and support of program code.

We have already had business with library: in order to start using functions of input/output in C it is required to connect standard library of input/output — stdio.h.


Library creation

The first step in creating own library will be excretion of set of functions in separate file with its further connection to the main program. Let us consider simple program that reads an array from file, unfolding this array and writing result to another file. After excretion of certain function we achieve next file main.c:

#include <stdio.h>
#define MAX_SIZE 100
 int arrayScan(FILE *in, int array[], int limit){
int len = 0;
for (; len < limit && fscanf(in,"%d",&array[len])=1;len++);
return len;
}

void arrayReverse(int array[], int size) {
for (int lo=0, hi = size -1; lo<hi; lo++, hi__){
int buffer = array[lo];
     array[lo]=array[hi]
array[hi]=buffer;
}
}
void arrayPrint(FILE *out, int array[], int size) { 
int last = size-1;
for (int i=0; i < last; i++){
frintf(out,"%d", array[i]);
}
frintf(out,"%d\n", array[last]);
}
int main() {
FILE *in = fopen("task in","r");
FILE *out = fopen("task out","w");
int array[MAX_SIZE];
int size;
  size = arrayScan(in, array, MAX_SIZE);
fclose(in);
  arrayReverse(array,size);
arrayPrint(out,array,size);
fclose(out);
return 0;
}

Now, let us take away function for array processing in a single file and name it array.c:

#include <stdio.h>
int arrayScan(FILE *in, int array[], int limit){
int len = 0;
for (; len < limit && fscanf(in,"%d",&array[len])=1;len++);
return len;
}

void arrayReverse(int array[], int size) {
for (int lo=0, hi = size -1; lo<hi; lo++, hi__){
int buffer = array[lo];
     array[lo]=array[hi]
array[hi]=buffer;
}
}
void arrayPrint(FILE *out, int array[], int size) { 
int last = size-1;
for (int i=0; i < last; i++){
frintf(out,"%d", array[i]);
}
frintf(out,"%d\n", array[last]);
}

Pay attention, that in this file we use functions for working with files, therefore it is necessary to connect in it particular library. Well, in our file main.c remains next code:

#include <stdio.h>
#include "array.c"
#define MAX_SIZE 100
int main() {
FILE *in = fopen("task in","r");
FILE *out = fopen("task out","w");
int array[MAX_SIZE];
int size;
  size = arrayScan(in, array, MAX_SIZE);
fclose(in);
  arrayReverse(array,size);
arrayPrint(out,array,size);
fclose(out);
return 0;
}

Despite the facts that in the connected file array.c we have already specified headline name stdio.h it is considered to be common rule to provide with connection all libraries used in one or another file.

Take attention, that during connection of user’s libraries their names are better to specify in double quotes, not in angle brackets. Otherwise, the compiler will look for the connected library not in the project’s directory but following standard paths.

Naturally, an attempt to assembly separately file array.c will not be successful, since the compiler won’t find in it function main().


Function’s prototyping

he more functions we use the more frequently we face with situation when one function calls another. Since functions usage can be made only after they have been declared, it becomes more difficult to track correct order of its fetching. An extreme case can be considered as cross-recursion, when two or more functions are calling for each other — wherein, independently to which of them being pointed as first the code will not compile anyway.

int foo(int n) {
if (n<=0) {
return 1;
}
return n + bar(n-1);
}
int bar(int n) {
if (n<=0) {
return 42;
}
return n * foo(n/2);
}

Solution for this problem consists in division of function declaration from its realization. Wherein the function’s heading without its further realization is called prototype.

Function’s prototyping in C or C++ language is called a function declaration but without containing function’s body, but specifying function’s name, arity, arguments types and returned data type. While function definition describes what this function exactly doing, function prototype can be considered as a description of its interface.

Let us use this example to solve our cross problem:

int foo(int n);
int bar(int n);
int foo(int n) {
if (n<=0) {
return 1;
}
return n + bar(n-1);
}
int bar(int n) {
if (n<=0) {
return 42;
}
return n * foo(n/2);
}

Pay attention, that specifying parameter’s name in prototype is not necessary; however, if such action will simplify understanding of received parameters it should not be neglected.

Prototype represents some sort of a peculiar promise to compiler, that such function will be declared in further code. In order to make this work it needs those functions’ types of returned values in prototype and in realization to be the same, otherwise the compiler would not understand what it is supposed to do.

Since there is no performed code in prototype it also cannot hold any callings to other functions. It eliminates us from restrictions on the functions’ sequence on condition that all subjected to use functions will be declared in the form of prototypes before their invocation.


Headlines separation


Let us separate our future library into header file and realization. It will give us opportunity to skim up contents of the library without going into the details. Header information, to be exact — connected libraries, macros and functions prototypes let us take out to array.h file leaving just function realization. Thus, file array.h will look like this:

#include <stdio.h>

int arrayScan(FILE *in, int array[], int limit);

void arrayReverse(int array[], int size);
void arrayPrint(FILE *out, int array[], int size);

Basically, names of functions parameters are not required in this file, however, it is necessary to take into consideration that write int array[ ] without setting the name will take form of int* — which cannot be clear enough in the library for arrays processing.

In the file realization let us leave only connection of our header:

#include "array.h"
int arrayScan(FILE *in, int array[], int limit){
int len = 0;
for (; len < limit && fscanf(in,"%d",&array[len])=1;len++);
return len;
}

void arrayReverse(int array[], int size) {
for (int lo=0, hi = size -1; lo<hi; lo++, hi__){
int buffer = array[lo];
     array[lo]=array[hi]
array[hi]=buffer;
}
}
void arrayPrint(FILE *out, int array[], int size) { 
int last = size-1;
for (int i=0; i < last; i++){
frintf(out,"%d", array[i]);
}
frintf(out,"%d\n", array[last]);
}

Finally, let us point out in our main program the header connection:

#include <stdio.h>
#include "array.c"
#define MAX_SIZE 100
int main() {
FILE *in = fopen("task in","r");
FILE *out = fopen("task out","w");
int array[MAX_SIZE];
int size;
  size = arrayScan(in, array, MAX_SIZE);
fclose(in);
  arrayReverse(array,size);
arrayPrint(out,array,size);
fclose(out);
return 0;
}

It may seem strange, that have not connected file of realization array.c. How the compiler will know where it should take code of these certain function from? To solve this question, let us specify during compilation all used files with initial code (but not headings).

gcc -std=c99 main.c array.c

Thus, during the assembly the compiler will know from the header files that we will use certain set of functions, but the code itself it will take from array.c.


Separate compilation

Since we are planning to use our library in different programs it may be relevant using compilation separately from source functions code. However, as we remember, the file array.c will not assemble into working program itself due to absence in it of main performed function. It will be more appropriate that assemble represents not the only process, but several more or less independent procedures. One of them, as a matter of fact, is compilation — converts source file into code, and the second one is called linking, it performs general assembly of the final program from compiled parts.

The key will show compiler that it is necessary to perform only compilation without linking. It will give opportunity to compile functions code separately.

gcc -std=c99 -c array.c

As the output we will achieve object file — array.o. This is compiled code of our library. As a matter of principle, using analogue approach we may compile main.c not linking it wo library.

gcc -c main.c

The next step is to link everything together:

gcc main.o array.o

It is not like for our microscopic library, which includes three functions, the separate compilation benefits much, but if start talking about large project, well in advance assemble of separate parts will let you to save time. Because, after its further development we will be subjected to compile only changed modules and modules they depend on. Moreover, having compiled library’s code separately from the program we can track down errors in the library itself, abstracting from possible mistakes in other modules.