What are static and dynamic libraries in C?

Sandra Macias
5 min readMay 5, 2020

--

library directory

Libraries and functions

Good coding practices state a program should perform a single task very well. If you follow this rule, you are working in modules and you will be able to reuse this file in the future in a more complex program or to debug it more easily if it’s not working the way it should. There are some tasks that, as a programmer, you will have to perform over and over again. For example, add a node to a list, free memory in a c program, check if a letter is upper or lowercase, etc. Those programs are called functions. A good programmer will optimize each one of the functions and will save it for a future occasion. A very effective way to easily access these functions is by creating libraries.

There are two kinds of libraries: standard and user-created libraries. Standard libraries are public libraries that any user can call from their programs by adding a reference to it in the header of a program or in a separate header file. You can identify them because they end in .h. User-created libraries can be static or dynamic (AKA shared) libraries. In order to choose the most appropriate, it is useful to understand how they are built and how they are incorporated into an executable file.

How to make a static library

1. In the main program, include your header file (a file where you include a list of all the standard libraries, user-created libraries, and the prototypes of the functions that you are using.

Normally, when you compile a function file without a library, you use this script:

gcc function.c main.c library.h -o function_exe

where function.c is the file with the function you added to the library, main.c is the main file where you are calling this function, library.h is the header file where you include the prototype of the function (not the actual function)and function_exe is the name of the executable file.

The problem with this approach is that when your list of functions gets too long, you might omit one by mistake or you might not find it and this will lead to compilation errors.

First of all you need to compile the files that will be part of the library into object files (with extension .o).

gcc -c function.c -o function.o

Then, you can create the library by using this command:

ar rcs libraryname.a function1.o function2.o function3.o

the r flag means that any object file that already existed will be replaced, c means that the library will be created and s will create an index file with the object files

If you didn’t create this index you can create it later using the command ranlib.

If you want to use a library in a program, this is the command you need to use:

gcc programname.c -L libraryname.a -o main

-L is the flag that indicates that you want to link a library and main is the resulting executable file.

***

As you can observe, the executable file includes the function at the moment of compilation. Thus, this file will take more space but the execution time will be lower because the function has already been compiled into the executable. This also guarantees that if the library or the function you are calling is deleted or is damaged, your executable won’t be affected because it doesn’t require it any more after it has been compiled.

As you could see in the previous cases, if you need to modify any of the function files, you need to update the library and you need to compile again. So if you are using function files that are constantly being updated, you might want to consider creating a dynamic library instead.

How to make a dynamic library

Let’s look now at how to create a dynamic library in an example.

Suppose you have all these function files:

pesos_to_dollars.c

euros_to_pesos.c

dollars_to_euros.c

You have created these function files which perform conversion tasks. You want to create a library with them. But, you are planning to keep adding to these collection of conversion files, and create a library called libconversion. These are the steps you need to follow:

  1. Compile each one of the files using this script:

gcc -fPIC -c pesos_to_dollars.c

gcc -fPIC -c euros_to_pesos.c

gcc -fPIC -c dollars_to_euros.c

where -fPIC means position independent code, which is a requirement of shared libraries, and -c creates an object file.

2. Compile the library adding the three functions you want to include

gcc -shared -o libconversion.so *.o

3. Create a header file where you include the prototypes of the functions you want to create and the standard libraries you used.

header.h

#include <stdio.h>

int pesos_to_dollars(int n);

int euros_to_pesos(int n);

int dollars_to_euros(int n);

__________________

4. Compile the file in which we want to use the library. This will produce an object file.

gcc -c main.c.

5. Compile the object file to link with the library and get the executable file

gcc -o test test.c libconversion.so

6. Install library so that it can be found at the time of execution and they are linked to a group of default directories. By default the dynamic loader looks for the library in specific directories: /usr/lib/, /usr/X/lib, etc. You need to use this command to save your library in one of those directories.

sudo mv libconversion.so /usr/lib

Additionally, you need to run this command, ldconfig, which creates the necessary links and cache to the most recent shared libraries found in the directories specified on the command line, in the file /etc/ld.so.conf, and in the trusted directories (/usr/lib and /lib).

sudo ldconfig

If you are storing your library in a different path, you need to export its path to the environment variable. You can use the 'LD_LIBRARY_PATH' environment variable to tell the dynamic loader where to look.

First, you need to check if the environment has been created by using this command:

echo $LD_LIBRARY_PATH

If it hasn’t been defined, use this command:

setenv LD_LIBRARY_PATH /full/path/to/library/directory

If it has already been defined use:

setenv LD_LIBRARY_PATH /full/path/to/library/directory:${LD_LIBRARY_PATH}

or in bash, the first option if it is undefined, and the second, if not:

LD_LIBRARY_PATH=/full/path/to/library/directory
export LD_LIBRARY_PATH
LD_LIBRARY_PATH=/full/path/to/library/directory:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH

Finally, with this command (List Dynamic Dependencies), you can check if the linker can find the library:

ldd prog

Notice that this library will be very easily updated and the executable won’t require being recompiled since it is only linked at the moment of execution. This means that the executable will be lighter but the execution and loading time will be longer because that’s when the library will be linked. There is another disadvantage: if there is any problem with the path to the directory where the library is located, or if the library file is damaged, the executable won’t work either.

Summary: executable files linked to a static library is heavier and longer to compile but it will execute faster and you don’t have to worry about access to the library at the moment of compilation. Executable files linked to dynamic libraries are faster to compile and are smaller in size but they will take longer to load and execute because they are linked to the libraries at the moment of execution and there might be problems accessing the libraries.

More information:

man page ldd, man page ldconfig, http://docencia.ac.upc.edu/FIB/USO/Bibliografia/unix-c-libraries.html#using_static_archive

--

--