C Static Libraries

Estefano Misme
5 min readJul 11, 2021
Original photo by Nicolas Economou / NurPhoto via Getty Images on NBC | Edited by the author

In programming, libraries are files that store various functions that will be used in the execution of a program. There are two types of libraries: static and dynamic. In this article we will focus on static libraries.

What are they for?

In general, libraries are useful tools that help us save time and space within our program. If we store all our functions in a library (or at least the ones that we are going to use the most), we can make use of these by calling them directly in any part of our main file that we need. In this way, we do not have to write the entire structure of said function repeatedly in our code, making the program larger and slower than it should be.

Furthermore, static libraries have the peculiarity that, when compiled together with the main file, all the functions of the library are copied into the new executable output file. Then it will not be necessary later to have the library in the system for this executable to work. Which is not the case with dynamic libraries.

One drawback of static libraries is that, when copying the functions into the main file before being executed, if one of these functions needs to be changed in the future, we must create a new executable to be able to use the updated functions. Therefore, its use is not recommended for large programs, as it slows down the process for the final compilation.

How to create libraries?

We will do this from the Linux interface.
We will explain it with an example. We have the following functions:

xxx@xxxxx:~/sample/new$ ls
function1.c function2.c function3.c
xxx@xxxxx:~/sample/new$

whose content are the following:

xxx@xxxxx:~/sample/new$ cat function1.c
#include <stdio.h>
void _function1()
{
printf("This is a function.\n");
}
xxx@xxxxx:~/sample/new$ cat function2.c
#include <stdio.h>
int _function2(int a)
{
printf("%d", ++a);
return a;
}
xxx@xxxxx:~/sample/new$ cat function3.c
#include <stdio.h>
char _function3(int ascii)
{
printf("%c", ascii);
return ascii;
}
xxx@xxxxx:~/sample/new$

If we want to create a library from these functions, we must first create a header file .h with the prototypes of each function. This is simply putting the name of each function (not the content) with its arguments.

xxx@xxxxx:~/sample/new$ ls
function1.c function2.c function3.c headerfunctionsexample.h
xxx@xxxxx:~/sample/new$ cat headerfunctionsexample.h
void function1();
int function2(int a);
char function3(int ascii);
xxx@xxxxx:~/sample/new$

After this, we compile the functions to obtain an object file for each function, which we will do with the “-c” flag of the “gcc” compiler.
These object files contain the functions decoded in machine language so that the system can read them correctly. Its default extension is “.o”.

xxx@xxxxx:~/sample/new$ ls
function1.c function2.c function3.c headerfunctionsexample.h
xxx@xxxxx:~/sample/new$ gcc -c function1.c function2.c function3.c
xxx@xxxxx:~/sample/new$ ls
function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h
xxx@xxxxx:~/sample/new$

Now we can create our static library. We will do it with the “ar” program (a creator, editor and file extractor) and its “r” flags (which allows inserting different files into our destination, or replacing old objects with new ones if they have the same name) and “c” (to create the file). Preferably, the name that we add to this new file should have the prefix “lib-”, to remember that it is a library.
Standard libraries have the extension “.a”. The result is as follows:

xxx@xxxxx:~/sample/new$ ls
function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h
xxx@xxxxx:~/sample/new$ ar rc libfunctions.a function1.o function2.o function3.o
xxx@xxxxx:~/sample/new$ ls
function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h libfunctions.a
xxx@xxxxx:~/sample/new$

Once the library is created, we need to index all the objects (functions) placed there, so that the machine can speed up the search for the functions that the program needs when completing it. This is done with the “ranlib” command.

xxx@xxxxx:~/sample/new$ ranlib libfunctions.a
xxx@xxxxx:~/sample/new$

In order to view the functions stored in the library, we can use the “t” flag of the “ar” program.

xxx@xxxxx:~/sample/new$ ar -t libfunctions.a
function1.o
function2.o
function3.o
xxx@xxxxx:~/sample/new$

The existence of the objects (functions) indexed with the ranlib command within the library can be checked with the “nm” command, which shows us the objects and symbols indexed in the file that we indicate.

xxx@xxxxx:~/sample/new$ nm libfunctions.afunction1.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T _function1
U puts
function2.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T _function2
U printf
function3.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T _function3
U putchar
xxx@xxxxx:~/sample/new$

If all the functions are correctly identified in the library, then it is ready to be used.

How to use libraries?

To use the new library in any main file that intends to execute them, it is enough to compile both together.
For our example, we have created a file called main.c which will execute the functions we have created. Let us also remember which are the functions that we have previously created.

xxx@xxxxx:~/sample/new$ ls
function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h libfunctions.a main.c
xxx@xxxxx:~/sample/new$ cat main.c
#include <stdio.h>
#include "headerfunctionsexample.h"
int main()
{
function1();
function2(4);
putchar('\n');
function3(64);
putchar('\n');
return (0);
}
xxx@xxxxx:~/sample/new$
xxx@xxxxx:~/sample/new$ cat function1.c
#include <stdio.h>
void _function1()
{
printf("This is a function.\n");
}
xxx@xxxxx:~/sample/new$ cat function2.c
#include <stdio.h>
int _function2(int a)
{
printf("%d", ++a);
return a;
}
xxx@xxxxx:~/sample/new$ cat function3.c
#include <stdio.h>
char _function3(int ascii)
{
printf("%c", ascii);
return ascii;
}
xxx@xxxxx:~/sample/new$

So, we are going to create an executable called “exefunctions” that shows the expected result of the interaction of the main file with the library. To do this, we will use the “o” flag of the “gcc” program.

xxx@xxxxx:~/sample/new$ gcc main.c -o exefunctions libfunctions.a
xxx@xxxxx:~/sample/new$ ls
exefunctions function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h libfunctions.a main.c main.o
xxx@xxxxx:~/sample/new$

So when we run the “exefunctions” program, we will realize that it does correctly display the result we expect.

xxx@xxxxx:~/sample/new$ ./exefunctions
This is a function.
5
@
xxx@xxxxx:~/sample/new$

That’s all? No.

Do you remember that at the beginning it was said that the executables that were the result of compiling a static library with a main file copied all the functions of the library within themselves and, therefore, no longer needed the library to be executed? Well, we can check it out right away.

First, let’s move the executable to another directory. We are going to create a new folder within the current one, where we will place the executable and check if it continues to work.

xxx@xxxxx:~/sample/new$ ls
exefunctions function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h libfunctions.a main.c main.o
xxx@xxxxx:~/sample/new$ mkdir eee
xxx@xxxxx:~/sample/new$ mv exefunctions eee/
xxx@xxxxx:~/sample/new$ ls
eee function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h libfunctions.a main.c main.o
xxx@xxxxx:~/sample/new$ cd eee
xxx@xxxxx:~/sample/new/eee$ ls
exefunctions
xxx@xxxxx:~/sample/new/eee$ ./exefunctions
This is a function.
5
@
xxx@xxxxx:~/sample/new/eee$

In the same way, we can verify that the executable continues to work if we delete the header file and the static library.

xxx@xxxxx:~/sample/new/eee$ cd -
/home/vagrant/sample/new
xxx@xxxxx:~/sample/new$ ls
eee function1.c function1.o function2.c function2.o function3.c function3.o headerfunctionsexample.h libfunctions.a main.c main.o
xxx@xxxxx:~/sample/new$ rm libfunctions.a headerfunctionsexample.h
xxx@xxxxx:~/sample/new$ cd -
/home/vagrant/sample/new/eee
xxx@xxxxx:~/sample/new/eee$ ls
exefunctions
xxx@xxxxx:~/sample/new/eee$ ./exefunctions
This is a function.
5
@
xxx@xxxxx:~/sample/new/eee$

--

--