C Static Libraries
As your ability to code C projects increases so will the complexity and size of the programs you write. This increased complexity in code will eventually begin to affect your C program by increasing the amount of time it takes to compile your program and by increasing the linking time in the programs linker phase of compilation. If your C program has reached this point of complexity it may be time to consider breaking your C progam up into smaller programs that can be statically linked together during the linking phase of compilation by using a static library.
A static library is file that contains the definitions for predefined functions and variables that your C program will need at runtime to complete its tasks. Before the compilation process the library will consist of object codes created by the compilation process when compilation is haulted just before the linking stage. The library is indexed meaning that every function or variable defined in the library will be easily located by your C program. The static library is compiled into the final executable file created by the compiler and indexed for easy access. You can see a visual representation of this at the top of the page of this post. The Static library is sent to the compiler along with you C project code (Application code in diagram) and the compiler produces a final executable file containing the fully compiled static library and fully compiled C project code. The static library is indexed before compilation to make it easy to look inside the library and find the function and variable definitions as needed by the program. Because the library contains multiple predefined functions and variables and keeps them indexed for easy access and includes them in your executable file this makes a faster way to link programs together in an executable than if the functions and definitions were seperate on the disk. Because these object codes for the static library are compiled into the final executable the functionality of the static library is included in the executuble file at compilation. The static library is needed by the program because the main.c file calls functions that need to be defined and instructions that need to be carried out. The static library is linked and copied into the executable file along with the fully compiled main.c file. and the object code is not needed at runtime to produce a functional program.
Because a static library is created and its object code (the library itself) is linked into the program during the linking phase of compilation this means that every executable file created with a static library will carry along with it a full copy of the static library in its executable file. This is different from the shared libraries (sometimes called dynamic libraries) like the standard <stdio.h> library often used in c projects. Shared libraries like these are not included in the executable file during the compilation process like a static library would be. Instead, when your C executable program begins to run and uses a shared library a program on the computer called the dyamic loader takes over and looks at all of the shared libraries that your C program is utilizing in its code and then it loads those libraries into memory on the computer. The dynamic loader loads a copy of every shared library listed by the program and attaches these to a copy of the C program. Shared libraries can be useful because the shared library is not loaded into the execuable like a static library would be rather they are loaded in your computers memory and refered to by the C program while it is running. This means that if you write multiple C programs that all utilize the same shared libraries you do not need to have the shared library included in every execable file you create. Multiple programs can utilze the same copy of the library stored in computer memory. You can now use far less memory to run your executables and your executables will be much smaller all while saving up disk space for other great programs that you create.
Now that we have a better understanding of what a static library is in C programming we can talk about how to go about create a static library for your C projects and how to get it included in your final executable files.
To create a static library for your project you will need to utilize a program called ‘archiver’. The ‘archiver’ program is very useful in that it allows you to create and maintain archive files full of the object files that define the functions in your static library. The ‘archiver’ program can also be used to modify object code in the library, list the names of the objects in the library, change the orders and positions of the members or different functions in the library and in general manipulate the different members that make up the static library before the final compilation into the executable. The first step in creating your static library is gathering together all of the functions and variables you would like to include in your static library in the directory that your working on your project in. Your functions at this point will be written into .c files, or files ending with the .c extension. In order to create object files from all of your .c files you must first compile your .c files with a special flag, the -c flag, that tells the compiler to compile all the .c files to the point of creating object files (files ending in .o) and stop the compilation process before it enters the last phase of compilation called the linker phase. Since the compilation process is stoped before the final linker phase of the compilation the ouput of the compilation process are .o files or object files. Lets look at what this process will look like.
Below is an image showing a current directory complete with the .c files of all of the functions that you want to include in your archive.
You can see in the image above that there are multiple .c files in the directory that we would like to create a static library from. Creating the .o files from this archive is easily done with one of the flags used in the gcc command. To create the .o files from these .c files you need only to run the following command while in this directory.
This command begins the compilation process by calling the gcc program to begin compilation. The -Wall flag tells the compiler to rearrange the order in which you have declared varibles in your c files to match the order in which they are utilized in the c file. The term -pedantic refers to files called ISO files and this flag was included to prevent compilation of programs with forbidden extensions or to prevent unwanted programs to be included in the archive. The flag -Werror treats all possible warnings as an error and stops the compilation process until you can fix the issue. The -Wextra flag creates additional warning based around pointers and if they are compared against integer values. Wextra also creates additional warnings for c++ projects as well. The -c option is the option that tells the compiler that we are not interested in fully compiling the code to an executable file yet but that we would like the compiler to compile these .c files to object code (files that end in .o) and and stop the compilation process before it enters the final linking phase. The *.c command is telling the compiler to compile all files in the directory that end with a .c extension. The result of running this command on the directory in the image above is to create .o files for every .c file in the directory. Below is a picture of what the directory will look like after the the .c files are compiled to the object compilation using the bash “l” command that lists files in the current directory.
Now that we have created .o files for every .c file containing our functions we can begin the process of getting those newly created object files into an archive file. To do this we will utilize the ‘archiver’ program and it’s functions. Below is a picture of the command that we will use to create the archive file.
The “ar” command tells the ‘archiver’ program that it is going to create the archive file. The flag -r is telling the ‘archiver’ program to insert the members of the file (the .o files of our functions that we intend to include in the archive) into the archive file in the order they are recieved and to replace older entries with the same names with newer ones. The flag c tells the ‘archiver’ program to create the archive if it does not already exist. In this example the the file name we are going to use for our archive will be
libholbertonschool.a with the .a extension showing us that this file as an archive file. The *.0 command at the end is calling out every file in the directory that ends in a .o extesion as we only want to include the object files of for our functions in the archive. After running this command we have created our archive file ending with a .a extension and we have put into that .a file a copy of all the .o files related to the functions we are interested in archiving.
Now that we have our archive full of our .o files we need to index every member of the archive file to make it easy for the function to be located when it needs to utilized by the program. The command below will do this for you and is neccesary to make the process work correctly.
Now that we have created the archive with all of the .o files related to the functions we are trying to archive we have finally reached the point where we can try compile our program and utilize the static library by compiling both files into an executable file for the outside world to use. To illustrate here is an example of a possible main.c file that we will use along with our static library to compile an executable file we will call “alpha”.
To compile this main.c file we need to include the definition of the print_alphabet function in the static library so that when main.c calls the print_alphabet function the program can find the definition in the indexed archived static library and perform the function, in this case printing the alphabet.
To compile the main.c file along with the static library archive into the executable you use this command.
The command gcc starts the compilation process. Next, is the main.c file we are looking to compile. The -L tells the compiler to add the folowing directory to the list of directories to be searched for by the main.c file adding a new location to search for directories by the program. This is now followed by -lholbertonschool where our .a static library file named libholbertonschool.a is called out. Notice that the prefix “lib” in the .a file name is removed in the command and prepended with -l flag to signal to the compiler the the static libarary file libholberton.a is the static library to be included in final compiled executable called “alpha”. After the compiling process is completed we can now run our executable file “alpha” by typing this command.
The executable file runs showing that the function of print_alphabet in our static library called by our main.c file and is working correctly. Both the static library and the main.c file are compiled into the final executable “alpha” and the static library is correctly indexed for the program to access the function of print_alphabet.