CStatic & Dynamic Linking

Static & Dynamic Linking

Once code is compiled into object files, it's often packaged into a library so it can be reused across multiple programs without recompiling it every time. C supports two fundamentally different kinds of libraries — static and dynamic (shared) — and understanding the trade-off between them matters for binary size, startup time, and how you ship updates.

Static Libraries

A static library (a .a file on Linux/macOS, .lib on Windows) is essentially a bundle of pre-compiled .o object files. When you link a program against a static library, the linker copies the needed code directly into your final executable. The result is a single, self-contained binary with no external dependency on that library at runtime.

Bash
# Create a static library from object files
gcc -c mathutils.c -o mathutils.o
ar rcs libmathutils.a mathutils.o

# Link a program against it statically
gcc main.c -L. -lmathutils -o program
Dynamic (Shared) Libraries

A dynamic or shared library (.so on Linux, .dylib on macOS, .dll on Windows) is not copied into the executable. Instead, the executable just records that it needs the library, and the operating system loads it into memory at runtime, when the program starts (or even later, if loaded on demand).

Bash
# Create a shared library (position-independent code is required)
gcc -c -fPIC mathutils.c -o mathutils.o
gcc -shared mathutils.o -o libmathutils.so

# Link a program against it dynamically
gcc main.c -L. -lmathutils -o program

# At runtime, the OS needs to find libmathutils.so, e.g.:
export LD_LIBRARY_PATH=.
./program
Static vs Dynamic: A Comparison

Aspect

Static linking

Dynamic linking

Binary size

Larger (library code is embedded)

Smaller (library stays external)

Startup time

Slightly faster (no library loading step)

Slightly slower (OS must locate/load the library)

Updating the library

Requires recompiling/relinking the executable

Replace the shared library file; the program picks it up automatically

Memory sharing

Each process gets its own copy of the code

Multiple processes can share one in-memory copy of the library

Deployment

Single self-contained binary, easy to distribute

Must ensure the correct shared library version is present on the target system

Choosing Between Them
  • Static linking is attractive when you want a single, portable binary with no external dependencies — common for command-line tools you hand out to people who may not have the right libraries installed.

  • Dynamic linking is attractive when many programs on a system share the same library (like the C standard library itself) — it saves disk space and memory, and lets you patch a security bug in the library without recompiling every program that uses it.

  • Most operating systems dynamically link the C standard library by default, even for programs you compile yourself, unless you explicitly request a fully static build.

Forcing a Fully Static Build

Bash
gcc main.c -static -o program
Note
On some systems, fully static linking against glibc is discouraged or unsupported for certain features (like DNS resolution via NSS), since glibc's own dynamic-loading tricks assume a dynamic environment. It works fine for simple, self-contained programs, but be aware it's not universally trouble-free.
Tip
The "DLL Hell" / shared-library-versioning problems that dynamic linking can introduce (needing exactly the right version of a `.so`/`.dll` present) are a large part of why container technologies like Docker became popular — they bundle a whole consistent set of shared libraries alongside the application.
Warning
When creating a shared library, remember to compile the object files with `-fPIC` (Position Independent Code). Without it, the resulting `.so` may fail to build correctly or behave unpredictably, since shared libraries need code that works no matter where in memory the loader places it.