What is a C library?

A C library is a set of named functions, for example dbinit() or SQLConnect(). Or, for that matter, fopen(3)[34].

Libraries come in two flavors: static and dynamic.

Static libraries

Static libraries (also known as archives) have been around as long as C itself. Like a .zip file, they're just a bag of object files — containing functions, of course — with a table of contents in front giving the address of each name[35]. Static libraries are created from object files using a librarian utility of some kind. One such programs is ar, for archive.

Static libraries are part of the build environment. Functions in static libraries are joined to a program's main module by a static linker at build time to produce an executable program. The executable incorporates the libraries' object code into its own body, making it completely self-sufficient.

Dynamic libraries

Dynamic libraries are the new kid on the block, as these things go, arriving on the Unix scene circa 1985. Like a static library, a dynamic library is a collection of functions with a table of contents. They are referenced at build time to give the executable information about how they will eventually be used, but they aren't used until run time.

Dynamic libraries are part of the run-time environment. When a program is run, the run-time linker finds the dynamic libraries needed by the program, finds the addresses of the required functions, and assembles a runable image in memory. Missing libraries and/or missing functions — or the wrong versions of them — can lead to head-scratching and other amusing behavior.

In Windows® dynamic libraries are called dynamic link libraries (DLLs). In Unix they're normally called shared objects. But they're roughly the same thing.

[Note]What about .h files?

C header files include functional prototypes, declarations (not definitions) of functions. Functional prototypes describe to the compiler each function's parameters, allowing the compiler to confirm that the function is being called correctly.

Most of the functions declared in header files are implemented in libraries. However, there's no mechanical or automatic relationship between the functional prototypes in the header files and their implementation in a library. The .h file is maintained by hand, by the programmer, and is used to generate a library. The header file and associated library are distributed and installed together (one hopes), but correct installation and subsequent use by the compiler & linker require human beings to keep track of the pair. Failure to do so leads to interesting development and even run-time problems, especially with libraries whose functions' parameters change from version to version.

For example, imagine a function f(int g) defined in library libf.so and declared in f.h. In a later version of libf.so, the function's parameter is changed to use a pointer, f(int *p), and f.h is likewise updated. Possible errors that cannot be prevented by the linker include:

  1. An old program could use the new library. Probably the integer it passes will be interpreted as an out-of-bounds address, resulting in a segmentation violation.

  2. A new program could use the old library, passing an address that the library interprets as an integer. Hillarity ensues.

  3. Existing source code could be compiled using the old header file but linked to the new library. If you've never done that, give it time.

These errors are possible because C functions are identified to the linker by name only. On the upside, that makes the tools simple and easy to implement and, by the same token, simplifies the use of C libraries by other languages. The downside is that the work of ensuring that the right libraries are used becomes an administrative task instead of a technical one.

[34] The Unix convention is to put in parentheses behind the name the section of the manual in which the function is documented. FreeTDS functions don't get numbers because they're not in the manual. Yet.

[35] Or, depending on how you look at it, the name of each address.