C static libraries. A story short.

Carlos Alvarez
8 min readJul 14, 2019

--

The paradise will be a librarie

Why use libraries?

As we are doing computer programs, we realize that some parts of the code are used in many of them. For example, we can have several programs that use complex numbers and the functions of addition, subtraction, etc. are common. It is also possible, for example, that we like to make games, and we realize that we are repeating the code again and again to move an image (a Martian or Lara Croft) on the screen.

It would be great to be able to put those functions in a separate directory of the specific programs and have them already compiled, so that we can use them whenever we want. The huge advantages of this are:

Not having to rewrite the code (or do copy-paste).
We will save time compiling each time that code is already compiled. In addition, we already know that while we make a program, we try and correct, we have to compile among many and “many more” times.
The code already compiled will be tested and will be reliable. Not the first times, but when we have already used it in 200 different programs and we have been correcting the errors.

The way to do this is to make bookstores. A library is one or more functions that we have already compiled and prepared to be used in any program we do. You have to have enough eye when we do them so as not to put any dependence on something concrete of our program. For example, if we do our function of moving the image of Lara Croft, we will have to do the function in a way that admits any image, since Lara Croft would not hit us with jumps in a “space invaders” style game.

What is a C static librarie

In linux we can do two types of libraries: static and dynamic.

A static library is a library that is “copied” into our program when we compile it. Once we have the executable of our program, the library is useless (it is a saying, it is useful for other future projects). We could erase it and our program would continue to work, since it has a copy of everything it needs. Only that part of the library that is needed is copied. For example, if the library has two functions and our program only calls one, only that function is copied.

How we have to organize our code. How they work?

To be able to put our code in a library, we need to organize it in the following way.

One or more .c source files with the code of our functions.
One or more header files .h with the types (typedefs, structs and enums) and prototypes of the functions that we want to use.

Example file.h

library1.h
#ifndef _LIBRERIA_1_H
#define _LIBRERIA_1_H
int sum (int a, int b);
int subtraction (int a, int b);
#endif

Example, file.c

library1.c
int sum (int a, int b)
{
return a + b;
}
int subtraction (int a, int b)
{
return a-b;
}

An important detail to keep in mind, are the #define of the header file (.h). When making a librarie, we do not know in what future programs we are going to use it or how they will be organized. Suppose in a future program that there is a header file file1.h that #include of ours. Imagine that there is also a file2.h that also makes #include of ours. Finally, with a little more effort, imagine that there is a third file3.c that does #include of file1.h and file2.h, that is, more or less the following:

file1.h

#include <libreria1.h>

file2.h

#include <libreria1.h>

file3.c

#include <fichero1.h>
#include <fichero2.h>

When we compile file3.c, depending on what you have defined in library1.h, we will get an error. The problem is that when you include file1.h, everything in that file is defined, including library1.h. When file2.h is included, we try again to define what is contained in library1.h, and we get an error that these definitions are defined twice.

The way to avoid this problem, is to put all the definitions within a block #ifndef — #endif, with the name (_LIBRERIA_1_H in the example) that we like and different for each of our header files. It is common to put this name preceded by _, ending in _H and matching the name of the header file, but in upper case.

Within the block #ifndef — #endif, we make a #define of that name (it is not necessary to give it any value, it is enough that it is defined) and then we define all our types and prototypes of functions.

When we include this file for the first time, _LIBRARY_1_H will not be defined, so it will be entered into the block #ifndef — #endif and all types and prototypes of functions will be defined, including the same _LIBRERIA_1_H. When we include it for the second time, _LIBRARY_1_H will already be defined (from the previous inclusion), so it will not enter the #ifndef — #endif block, and nothing will be redefined for the second time.

It is a good habit to do this with all our .h, regardless of whether or not they are for bookstores. If you look at some .h of the system you will see that you have this kind of thing until you get bored. For example, in /usr/include/stdio.h, the first thing after the comments is a #ifndef _STDIO_H.

How to create them

Look us an graphic example

How to create own C statics libraries

Before we can create our static library, we must convert our C programs into object files. To do this, we will use the GNU GCC compiler with the — together with the wildcard * to match the files simultaneously

Now that we have compiled .o archives of objects for each of our C files, we can store them in a static library. To do this, we will use another GNU program: binary utility ar, a convenient abbreviation for “archiver”. Here, we will use ar to create a library, but the program is actually much more versatile; in addition, it can be used to modify and extract object files from a given file file.

To be thorough, and as good practice, we’ll run ar with two optionsr, which will replace any older object files located in the directory with our new ones, and c, which instructs ar to create our library only if it does not already exist. I’m going to call my library libholbertonschool.a (the lib prefix being standard naming practice for libraries). Again, I’ll use the * character to match all object files simultaneously.

Ok! So we have a static library libholbertonschool.a containing the object files *.o. To list the contents of our new library, run again with a new option, -t for list on tables in the terminal.

Now, how can we use the library?

As in a pile of disorganized books in the library, we will sort the libraries and index them with the command ranlib, Afterwards, we can view our newly-indexed library using the command nm

Note that the nm command lists the symbols contained in each object file. The letters next to each symbol indicate its type. in the image, the two letters: T, which indicate that a symbol is located in the text section of the file, and U, which indicates that the symbol is not defined.

We have converted our C programs into object files, store them in a static library and index our library, we can, finally, use the library in a convenient way to give access to the linker to all the files it contains. For example, we have a main program in C main.c that uses the defined functions print_alphabet ()

we could compile the program by naming all the relevant files in the compilation command. but with our new library, this process is compacted. Instead of listing each file, we will only include the name of the static library with the prefix of the -l option. After successful compilation, we can run our a.out executable

Note that in addition to the -l option, it was included -L., Which specifically indicates to the linker that libraries can be found in., The current directory. Note also that I left the lib prefix and extension .a outside the library name: the linker automatically attaches these strings to the file name when it searches for the library.

What exactly happened in the previous execution? In the binding phase of the compilation, the linker checks and adds files to the executable from left to right, one at a time. In our previous compilation of main.c, the linker first adds the main function assembled, then enters libholbertonschool.a, then reviews the object files within the library in the order in which they are indexed. As the linker locates the object files corresponding to the functions used in the main program, it adds them directly to the resulting executable. This static link process can be visualized like this

in the example, the libraries However, the linking process is related to the drawbacks. First, since the link is directly embedded with the machine code in the executable, C programs that involve many functions can result in large and difficult to manage executables. Second, for this same reason, static linking is not flexible when it comes to editing programs later. With static linking, any changes made to the original programs after compilation are not recorded in the existing executables; Recompilation is required.

The solution to this is dynamic linking; However, that is a subject for another time. For now, static libraries are useful tools in their own right, file archives that allow us to link C programs more quickly and efficiently. Now, go practice practicing your own static library

Bibliography

Command for copy /paste the files in de directory automatically:

#!/bin/bash
cp {$(find . -name “0-isupper.c”),\
$(find . -name “0-memset.c”),\
$(find . -name “0-strcat.c”),\
$(find . -name “1-isdigit.c”),\
$(find . -name “1-memcpy.c”),\
$(find . -name “1-strncat.c”),\
$(find . -name “100-atoi.c”),\
$(find . -name “2-strchr.c”),\
$(find . -name “2-strlen.c”),\
$(find . -name “2-strncpy.c”),\
$(find . -name “3-islower.c”),\
$(find . -name “3-puts.c”),\
$(find . -name “3-strcmp.c”),\
$(find . -name “3-strspn.c”),\
$(find . -name “4-isalpha.c”),\
$(find . -name “4-strpbrk.c”),\
$(find . -name “5-strstr.c”),\
$(find . -name “6-abs.c”),\
$(find . -name “9-strcpy.c”),\
$(find . -name “_putchar.c”),\
} 0x09-static_libraries/

--

--