:C language libraries, part 1, static libraries
compiler: gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
environment: vagrant virtual machine with linux 14.04.5 LTS for Ubuntu
language: C language
Have you been learning C language and using #include <stdio.h>
and have no idea what is happening with that? If so, then this post is for you! I will be discussing C language static libraries, why to use libraries, how they work, how to create them, and how to use them. For background information on the subject, it's helpful to understand what happens when C language files are compiled; for more information on that, check out my other blog post: Computer Compilers: brief introduction, which helps to explain how the gcc compiler works. As this is the first blog of a two part series, please refer to my other post on libraries: what the f*Lib.so? for more on dynamic libraries.
In the above referenced post on computer compilers, you will see that during the gcc compiling process, the included header files such as <stdio.h>
and <stdlib.h>
have the effect of bringing new code into your '.c' file. The '.h' files refer to the header files and and these files include all the function prototypes of that library. Here is an example of an excerpt from the source code for 'stdio.h
' : _CRTIMP int __cdecl f printf (FILE*, const char*, ...);
. There, you can see the prototype for a common function: printf()
. However, the header file does not contain the actual code with the instructions for the prototype printf()
nor any other instructions for the other prototypes. C library files contain the actual code in object code format, for the functions a ‘.c’ program references in the header files.
library analogy to explain libraries
To help explain the difference between header files and library files in C language, an analogy of a traditional Library with books is helpful. If you were born before the 1990's it's likely you are familiar with the dewey decimal system, and the notecards that contained every book in the library with it's dewey decimal classification and a brief bibliography.
So we can view the Library in 4 sections: the notecard with dewey decimal classification number, the drawers that hold the notecards, the shelves that contain books, and the books themselves. That notecard is like the prototype, the "header files" in C language are like the notecard containers or drawers. The book in the library analogy, is the actual code and instructions for each function, which are contained in shelves, which are the libraries in C language.
why use libraries?
Library files typically contain the code for functions that are most commonly used by an application or programmer, or that belong to a unique set of functions that together work to meet a larger end goal that can be used in a variety of different applications. Thus, libraries in C language help programmers save time by linking those common functions instead of having to hard code those same functions every time a programmer needs to use them in different applications and situations. Also, by keeping the most common functions separated into libraries, it helps programmers to stay organized. Programmers can share a set of functions in a libraries without adding any other of their unique code to the library as well. Another advantage is being able to modify library files without impacting your main() function or your executable file. For example, if a programmer ever updates the code from a library, it is possible to make these changes without effecting the executable files that uses the library's functions and without having to integrate the changes into all the files that use the libraries; this depends on the way one compiles a program and links the libraries. In the linking phase of gcc, which comes after the preprocessor phase when header files are actually copied into your source code, libraries are “linked” either dynamically or statically.
what is a static library?
In the above examples, I was using printf()
from the stdio.h
header file and C standard library. When compiling your code, using the GNU Compiler Collection (gcc
), for the C standard library, the compiler usually defaults to including it as a dynamic library. A dynamic library is essentially the same content as a static library; however, it is linked in a different way. With dynamic libraries, they are not included into your executable file, but rather linked to the file and become loaded when the executable file is loaded. With dynamic linking, the library is only loaded once on your CPU memory and any program that references the library with dynamic linking has access to that part of memory. However, with static libraries, the code for all the functions in a C library that the program uses is actually included in every executable file in the linking phase of the compilation process. Therefore with static libraries, if you have 2 separate programs running that use that same code from the functions in the included library, that library is taking up twice as much storage in your computer's memory.
code in a static library
Static library files typically end with '.a' extension for "Archive" library verses '.so' extension standing for "Shared Object" library. The ".so" extension is typical for dynamically linked libraries, which are loaded when an executable file is running. A static library contains object files ".o" extension, and to learn more about that you can see my blog post Computer Compilers: brief introduction.
how to create a static library
In the following code snippets, you will see how I have created a static library and then how I used the static library. Since I’m a student at bootcamp School in San Francisco, and since their program teaches us students to create our own C libraries, which I find to be exceptionally useful, I will give them a shoutout in my below examples.
source code
First, listed here are my ‘.c’ files and functions with the unique header ‘.h’ file that I will use in place of the C standard library. I also used cat
to show the code in each of the files.
$ ls _strncpy.c puts.c bootcamp.h _putchar.c $ cat bootcamp.h int _putchar(char c); void _puts(char *s); char *_strncpy(char *dest, char *src, int n); $ cat _putchar.c #include int _putchar(char c) { return (write(1, &c, 1)); } $ cat _strncpy.c #include "bootcamp.h" char *_strncpy(char *dest, char *src, int n) { int j; for (j = 0; j < n && src[j] != 0; j++) dest[j] = src[j]; for (; j < n; j++) dest[j] = 0; return (dest); } $ cat _puts.c #include "bootcamp.h" void _puts(char *str) { int i; for (i = 0; str[i] != 0; i++) _putchar(str[i]); _putchar('n'); }
gcc
In the next example, I will use the commands gcc -c *.c
and ar rc libbootcamp.a *.o
to create object files and to store them into a library called libbootcamp.a. Static libraries begin with "lib" and end with '.a' extension. The manual explains what 'ar' and 'rc' commands and flags do:
ar — create, modify, and extract from archives. r Insert the files member... into archive (with replacement). This operation differs from q in that any previously existing members are deleted if their names match those being added. c Create the archive. The specified archive is always created if it did not exist, when you request an update.
Here are the examples of my implementation of these instructions:
$ gcc -c *.c $ ls *.o _strncpy.o puts.o _putchar.o $ ar rc libbootcamp.a *.o $ ls *.a libbootcamp.a
confirmation
Now, I will run the optional command ranlib libbootcamp.a; the manual page for ‘ranlib’ has a very thorough explanation, which I will add here:
ranlib generates an index to the contents of an archive and stores it in the archive. The index lists each symbol defined by a member of an archive that is a relocatable object file. You may use nm -s or nm — print-armap to list this index. An archive with such an index speeds up linking to the library and allows routines in the library to call each other without regard to their placement in the archive.
Additionally, I will run this command: nm libbootcamp.a
, which is to "list symbols from object files" according to the above description and the manual on ‘nm’. This command will give us an example of some of the object code files in the libbootcamp.a file to verify success of our new library!
$ ranlib libbootcamp.a $ nm libbootcamp.a _strncpy.o: 0000000000000000 T _strncpy _puts.o: U _putchar 0000000000000000 T _puts _putchar.o: 0000000000000000 T _putchar U write
how to use a static library
I'll create a file called 'main.c' with my main function. This file will not include the C standard library, and instead I will use my custom library with my custom functions. The example still has the included header bootcamp.h
because that file contains the prototypes for the functions that I will use; however, all of the actual code for the functions is included in the .a
extention file that will be used when compiling the file. When the file is compiled, I will use the flags (or options) '-L.', '-l', and '-o'
to include my library of functions and make the output file called 'bootcamp'. From reading the manual on gcc, we can learn that the '-L' flag instructs gcc to search for a library in the specified directory. Since, I added the '.', this means the current directory. The '-l' flag/ option is described in the gcc manual as: -l library Search the library named library when linking
. So, we specify the library libbootcamp.a
; however, this option automatically prefixes the input code with 'lib' and suffixes it with '.a' and so those specifications are unnecessary to include in your command to compile with gcc. Here is an example of my main function and output after executing the file:
$ cat main.c #include "bootcamp.h" int main(void) { char string1[] = "bootcamp", string2[10], *p, i; p = _strncpy(string2, string1, 10); _puts("I just copied a string, see:"); _puts(string2); return (0); } $ gcc main.c -L. -lbootcamp -o bootcamp $ ./bootcamp I just copied a string, see: bootcamp