C Programming and Static Libraries
There is a saying in programming, “Keep your work, DRY.” While we all know that computers and liquids are not a good mix, this type of dry stands for ‘Don’t Repeat Yourself.’ It may seem like a simple enough saying, but when you are building large projects or even just starting, it’s far easier said than done. One solution to the very basics of this problem is the idea of not reinventing the wheel each time you need it. For example, it would be massively inefficient if you, as an individual programmer, had to explain in code to your computer how to do addition and subtraction each time you needed a bit of math for your program to work. Thankfully those of us coding today are standing on the shoulders of those that came before, and thanks to their frustration at repetitive, tedious coding, we have built-in logic and libraries to draw on.
So what is a library?
Basically, a library is a group of functions that have been written previously and compiled into a single source that you can all on in your current work. More technically, “A library is a file containing several object files, that can be used as a single entity in a linking phase of a program.” If you remember my last blog post about GCC and the compilation process, you’ll recall that the compiling of c files includes the linking phase, which incorporates c libraries such as stdio.h and stdlib.h. By having all of the functionality in a single library rather than many disparate files, we gain a speed advantage from only needing to look in one place, and thanks to compilation we get a further speed advantage by directly including our library functions in our main program. In short, it’s easier only to have to look in one place for what you need, and to bring everything you need with you rather than make several trips back and forth to the source.
Static vs. Dynamic Libraries
In C, we have two options when it comes to libraries, Static or Dynamic. A dynamic library (or shared library) is one that can be shared across countless C programs. Standards like standard input-output and math.h are both examples of dynamic libraries. These .h files are utilized by literally hundreds of programs, and once the dynamic library is loaded in memory by the first call, you only need a single copy. This is possible because of how dynamic libraries are utilized. First, when a dynamic library is called in a C file and the file is compiled, the linker verifies all the symbols required by the program are either linked into the program itself or in one of its shared libraries. During the linking process, this check of the associate libraries is just a check and not copying of the dynamic library into the executable file. When the executable is eventually run, what is called a dynamic loader checks what libraries are linked in the program, loads them into memory, and then links them with the copy of the executable in memory. Again, once a dynamic library is loaded into memory it may be accessed by multiple executables. This process does mean that files using dynamic libraries are a bit slower, but it is a negligible difference you are unlikely to ever notice and the savings in memory for not copying libraries into each and every executable file is well worth it.
Static libraries are similar in structure in that they are a collection of object files which a program can call on and utilize just by including the static library in the header. The difference from dynamic libraries is that static files are fully linked/copied during compilations and are thus not needed when the executable file is later run, all the details are already in the executable. This can lead to larger executable files but faster run times for our programs. One advantage of static files in this respect is that you already know that any changes are going to require you to recompile the whole program, and it is unlikely you would miss this or forget. For dynamic libraries, if we re-compile our executable and try to run a second copy of our program with the new lib, we may get stuck as the dynamic loader will find that a copy of the library is already stored in memory. This can lead to an older version of the library being attached to our program, rather than the new modified library.
So we know libraries are useful for programming as they save us time and resources, but how the heck do you make them? With the shell, of course! The first thing you’ll need to do is gather all the C functions that you want to have included in your new library. Next, we’ll want to convert them from .c files to .o (or object files). As we have learned in the past that can be accomplished by stoping the compiler during assembly with the following command:
$ gcc -c *.c
At this phase, all your files with a .c extension will now have a version that is .o. Next, we’ll want to put all our .o files into our new library. To do this we use the command ar (for archiver) with the following format:
$ ar -rc libholberton.a *.o
The archiver, called as ar, is a Unix utility that maintains groups of files as a single archive file. The above command creates a new archive file called libholberton.a and it puts all files with a .o extension into this libholberton.a. The c flag tells the archiver to create the file named libholberton.a and then to insert the objects, replacing older files where needed thanks to the r flag. Now that we have all our objects in our new library file, it’s time to get things organized. Just like a real library, we need to have things in proper order if we want to find anything. To straighten things up, we run this command:
$ ranlib libholberton.a
Ranlib is a function to create an index of objects in our libholberton.a archive. If we want to see the contents of the new library, you can use the
ar -t libholberton.a
Or, if we want to see the symbols in our library, we can use the use the nm command to list each symbol’s value, type, and symbol name from object files.
Using made libraries
So you’ve made a great new library full of useful functions that you’ll never have to write again. Now what? Well, we need to compile your library with a c file to make an executable forth. Get thee to GCC go!
For this process, we’ll run GCC with a few extra options. For instance, we want to use:
- -l<libraryname without the leading lib or the extension>
- -L : specifies the path to the library. For the current working directory, we can use -L. If your library is not in your current directory use the path format -L/home/tmp or where ever your library is.
gcc main.c -L. -lholberton -o executable
Finally, we can run our program with the standard:
Congratulations, you’ve now learned about, made, and utilized your own library!