Static Libraries in C Programming
Introduction
Before we talk about what a static library is, let us first talk about what a C Library is.
A Library is a file that contains several object files, which can be used as a single entity in the linking phase of the program. These object files are created by the assembler during the stage of compilation. These object files are simply object code/machine code of different C source files.
The object files are indexed in the library, making it easy to search/find symbols (functions, variables and so on) in them.
One advantage of using libraries is that linking a program whose object files are ordered in libraries is way faster than linking a program whose object files are separate on the storage. So it is best to create a library with all the object files that are on the storage before linking rather than to link all the object files individually together.
Also, using libraries, we have fewer files to look for and open, this further speeds up linking.
Although, there are two types of libraries:
Static Libraries
Shared/Dynamic Libraries
Our focus will be on the static libraries.
What is a Static Library?
Static libraries are a collection of object files linked into the program at the linking phase of compilation and are not relevant during runtime.
It is not relevant during runtime simply because when a program is linked with a static library, the linker copies the object files from the library into the executable file, and what this means is that this program will have its copy of the code and data from the library.
Static Libraries are used to share code between multiple programs, this helps to save time and effort in developing programs, as there will be no need to rewrite code or data.
How to Create a Static Library in C
To create a static library in C, we will need to make use of the archiver (ar) command.
This program is also used for:
Creating Static Libraries (which are archive files)
Modify object files in the static library.
List the names of object files in the library ( the nm and ar commands can also be used to list the symbols stored in a static library).
An Example of Creating a Static Library in C
Let us say we want to create a static library with the name libcalc.a
and we want to put the following object files inside calc_add.o
, calc_sub.o
, calc_mul.o
and calc_div.o
in it.
We will be doing this in a step-by-step approach.
Step 1
Create the object files if they don't yet exist. Here I am going to create these four files first as a C source file, before converting them to object files.
For calc_add
//calc_add.c int add(int x, int y) { return (x + y); }
For calc_sub
//calc_sub.c int sub(int x, int y) { return (x - y); }
For calc_mul
//calc_mul.c int mul(int x, int y) { return (x * y); }
For calc_div
//calc_div.c int div(int x, int y) { return (x / y); }
Step 2
Since we now have all our source code C files in a directory, we are going to define the function prototypes of the functions that are inside of the C source code files.
Now we need to create a header file and this header file will contain the prototype of all the functions of these source code files. Let's name the header file main.h:
//main.h #ifndef HEADER_FILE #define HEADER_FILE int add(int x, int y); int sub(int x, int y); int mul(int x, int y); int div(int x, int y); #endif
Our directory is now looking like this:
Step 3
In this next step, we are going to compile all our C source files in the directory into object code/files, because if you remember, we said that libraries are collections of several object files that are combined into a single entity.
The most beautiful this is that we can use a single command to compile all our C files into object files.
gcc -c *.c
The gcc is the normal c compiler, the -c is an option that can be used with the gcc to stop the compilation process at the assembler, that is after it is compiled to an object code (machine code).
From the image above, you can see that we now have our object code for each of our C files.
Step 4
It's now time to create our static library, from the example above, remember that we are to name our static library
libcalc.a
, so to create a static library with this name, we simply need to make use of the command:ar -rc libcalc.a *.o
Remember the
ar
? which is the program called Archiver that is used to create static libraries. The optionr
tells it in case there are older object files, they should be replaced with newer object files if available. The optionc
tells ar to create the library if it doesn't exist. And the*.o
is simply telling it to make use of all the object files in the current directory, as all object files end with a.o
.However, always keep to heart that in place of *.o you can specify the exact names of the object files including their .o format each following the other.
So, we now have our libcalc.a library file created.
Static Library Indexing
Sometimes after the library/archive is created, there is a need to index it, although in some systems, the ar
does it automatically when creating the archive, but there are still instances where one will need to do that, in that case, the ranlib
command comes into play. To make use of this command, we simply need to use it followed by the name of our library file.
ranlib libcalc.a
where ranlib
is the name of the command and libcalc.a
is the name of the library.
This indexing is important to address because if the modification time/date of a library file is older than that of the file stored on the file system, a compiler trying to use this library will complain that its index is out of date and it will exit. This can be solved with the ranlib
.
Another way this can be solved is by using the cp -p
command when copying library files (archive file), including all its attributes like the access, permissions, owner and even modification date and time, so this causes the compiler to think that the index inside the file is still updated.
How to use a Static Library in C
Now that we can create our static library, let us see how we can use that in our main program. So if you recall, we had to create a header file that contains all the prototypes of our functions, We are going to create a C file and we will be including our header file in the C file. In this main C file, we simply call our functions.
//main.c
#include "main.h"
#include <stdio.h>
int main(void)
{
int res1, res2, res3, res4;
res1 = add(10, 5);
res2 = sub(10, 5);
res3 = mul(10, 5);
res4 = div(10, 5);
printf("Sum = %d\n", res1);
printf("Difference = %d\n", res2);
printf("Product = %d\n", res3);
printf("Division = %d\n", res4);
return (0);
}
Your folder should contain these files.
Next, we will be using the command below to link the library file with our main program:
gcc main.c -L. -lcalc -o main
This will create a program called main
using the main.c
file and any symbols (functions, variables etc.) it requires from the calc
static library. The prefix lib
that we used for the library file i.e. libcalc
and the suffix .a
is not included when mentioning the library on the link command, and this is because the linker itself attaches these parts back to the name of the library to create a name of a file to look for.
The -L tells the linker that libraries might be found in the present directory, which is why it is followed by a full stop (.
), the full stop immediately after the L tells it to look for in the present directory that is in addition to the standard locations where the compiler looks for system libraries.
To run our file, we simply need to execute our main file, since that is what we specified to be our executive file while linking the static libraries.
./main
So this is how to work with static libraries.
The Importance of Linking Order
The order in which we present our object files and libraries to the linker is the order in which the linker links them into the resulting binary file.
The linker checks each file in turn, If it is an object file, it is being placed fully into the executable file, if it is a library, the linker checks to see if any symbols (functions, variables etc.) referenced in the previous object files but not defined in them are in the library if found (i.e. the symbol), the whole object file from the library that contains the symbol is being added to the executable file. This process continues until all object files and libraries on the command line are processed.
What this process means is that if library A uses symbols in library B, then library A has to appear on the link command before library B, otherwise symbols might be missing. The linker never turns back to the libraries it has already processed. So if in turn library B also uses symbols found in library A, then the library will have to be processed again for the second time, which in turn makes the linking slower. So it is a good practice to try as much as possible not to have such mutual dependencies between libraries, and if there exist any for any reason, then it's either redesign the library contents or simply combine both libraries into a larger library.
A good rule is to always mention the libraries after all the object files. For example, all our object files had a prefix
calc
and our library was also calledlibcalc
.
Conclusion
This brings us to the end of this article on static libraries, I believe we will talk about dynamic libraries or shared files/libraries in another article.
Thank you for reading. You can connect with me on Twitter and LinkedIn.
Subscribe to my newsletter
Read articles from Gideon Bature directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Gideon Bature
Gideon Bature
A Seasoned Software Engineer and Technical Writer.