Writing and Using Libraries in C - C Post 09

Libraries in C allow code reuse and modular programming. There are two types of libraries: static libraries and dynamic libraries. Understanding how to create, use, and manage them is crucial for efficient software development. This post will provide a comprehensive guide to building and linking C libraries, organizing header files, and dynamically loading libraries at runtime.


1. Static Libraries (.a Files)

Static libraries contain object files that are linked at compile-time, resulting in larger executables but faster execution since all required functions are included in the final binary.

1.1 Creating a Static Library

  1. Write the source code (math_functions.c):
#include "math_functions.h"

int add(int a, int b) {
    return a + b;
}
  1. Create a corresponding header file (math_functions.h):
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H

int add(int a, int b);

#endif
  1. Compile the object file:
gcc -c math_functions.c -o math_functions.o
  1. Create the static library using ar:
ar rcs libmath.a math_functions.o

1.2 Linking and Using a Static Library

To use the static library in a program (main.c):

#include <stdio.h>
#include "math_functions.h"

int main() {
    printf("Sum: %d\n", add(2, 3));
    return 0;
}

Compile the program linking with the static library:

gcc main.c -L. -lmath -o program

Run the program:

./program

2. Dynamic Libraries (.so or .dll Files)

Dynamic libraries are loaded at runtime, making executables smaller and allowing for updates without recompilation.

2.1 Creating a Shared Library

  1. Write the source file (math_functions.c): ```c #include “math_functions.h”

int add(int a, int b) { return a + b; }


2. Compile the shared object:
```sh
gcc -fPIC -c math_functions.c -o math_functions.o
gcc -shared -o libmath.so math_functions.o

2.2 Linking and Using a Shared Library

  1. Write a program that uses the shared library (main.c): ```c #include #include "math_functions.h"

int main() { printf(“Sum: %d\n”, add(4, 5)); return 0; }


2. Compile and link against the shared library:
```sh
gcc main.c -L. -lmath -o program
  1. Set the library path and run the program:
    export LD_LIBRARY_PATH=.
    ./program
    

3. Header File Organization

Properly structuring header files improves code maintainability and avoids circular dependencies. Best practices:

  • Use include guards or #pragma once to prevent multiple inclusions.
  • Separate interface and implementation by keeping function declarations in .h files and implementations in .c files.
  • Minimize unnecessary inclusions in headers.

Example:

// math_functions.h
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H

int add(int a, int b);

#endif

4. Dynamic Loading with dlopen()

C allows loading shared libraries dynamically using dlopen(), enabling plugins and modular applications.

4.1 Example: Loading a Shared Library at Runtime

#include <dlfcn.h>
#include <stdio.h>

int main() {
    void *handle = dlopen("./libmath.so", RTLD_LAZY);
    if (!handle) {
        printf("Failed to load library: %s\n", dlerror());
        return 1;
    }
    int (*add)(int, int) = dlsym(handle, "add");
    if (!add) {
        printf("Function not found!\n");
        return 1;
    }
    printf("Sum: %d\n", add(2, 3));
    dlclose(handle);
    return 0;
}

Compile and run:

gcc main.c -ldl -o program
./program

5. Static vs. Dynamic Libraries: Pros and Cons

Loading, please wait
Feature
Static Libraries
Dynamic Libraries
File SizeLarger ExecutablesSmaller Executables
Linking TimeRequired at Compile-TimePerformed at Load-Time
PerformanceFaster ExecutionMay Have Slight Overhead
UpdatesRequires RecompilationEasily Replaceable

When to Use Each?

  • Use static libraries when performance and independence from external dependencies are required.
  • Use dynamic libraries when reducing executable size and updating functionality without recompilation is needed.

Understanding how to create and manage static and dynamic libraries improves modularity and efficiency in C programming. By structuring header files properly and utilizing dynamic loading, developers can create flexible and maintainable codebases.

In the next post, we will explore secure and robust C programming practices, including buffer overflow prevention and safe memory handling.

Happy coding! 🚀




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Fourier Transforms - Seeing Sounds
  • Mathematics in Ancient Greece #4
  • Mathematics in Ancient Egypt #3
  • Mathematics in Ancient Mesopotamia #2
  • Mathematics in Prehistoric Times #1