The Big Picture: Two Ways to Use a Library

A programmer writes code, but does not write everything. To draw a window or read a file, they use ready-made library code. The question is: how does that library code get connected to the program? There are two answers.

An everyday picture: you are cooking a big dinner and need a recipe for sauce.

  • Static linking = you copy the recipe onto your own page. It is always with you, but your page is longer and your copy never updates.
  • Dynamic linking = you write "see the cookbook on the shelf, page 40". Your page stays short, but the cookbook must be on the shelf when you cook.

2.1 Static Linking: Code Copied In at Build Time

What it is: Linking is the last step of building a program. A tool called the linker joins your compiled code with the library code it needs. In static linking, the linker copies the needed library code into the EXE file.

 Your code (.obj)  ─┐
                    ├──> LINKER ──>  program.exe
 Library (.lib)    ─┘               (your code + a COPY of the library
                                     code, all in one file)

Terms used here:

  • Compiler: turns the programmer's text into machine code (.obj files).
  • Linker: joins .obj files and libraries into one finished program.
  • .lib file: a library for the linker. A static .lib holds the actual code to copy.

Good points:

  • The program is self-contained. Copy the one file to another PC and it runs.
  • No "missing DLL" surprise for that library.
  • The code can be tuned for that program, since only the parts used are copied.

Bad points:

  • The EXE is bigger.
  • Ten programs using the same library hold ten copies on disk and in RAM.
  • A bug fix in the library means rebuilding and re-shipping every program.

2.2 Dynamic Linking: Code Loaded Separately at Run Time

What it is: In dynamic linking, the EXE does not hold the library code. It only holds a note: "I need the function CreateFileW from kernel32.dll." When the program starts (or later), Windows finds the DLL and connects the two.

 Your code (.obj)  ─┐
                    ├──> LINKER ──>  program.exe
 Import library    ─┘               (your code + a list of
 (small .lib file)                   "I need these functions" notes)

 later, at run time:
 program.exe + kernel32.dll  ──> connected in memory by Windows

An import library is a small .lib file that has no code, only the names of the functions a DLL offers, so the linker can write the right notes.

Dynamic linking happens in two ways (details in Section 3):

KindWhen the DLL is connectedHow
Load-time (implicit)When the program startsThe notes (the import table) are filled in by Windows before the program's first line runs
Run-time (explicit)When the program asksThe code calls LoadLibrary and GetProcAddress itself

Delay-load: a middle way. The linker can also produce a delay-load program. The DLL is listed in the import table, but Windows connects it only the first time a function from it is called. That way a rarely used DLL costs nothing at startup. (Hedge: this is a Microsoft linker feature; it is not used by every program.)

2.3 Trade-offs: Size, Updating and Sharing

What it is: Neither way is "better". Each fits different needs.

Static linkingDynamic linking
EXE sizeBiggerSmaller
Disk use (many programs)Each has a copyOne shared DLL
RAM use (many programs)Each loads its own copyCode pages shared (Section 1.2)
Updating the libraryRebuild every programReplace one DLL
Start-upA little faster (nothing to find)Slightly slower (find and connect DLLs)
Missing file riskNone"DLL not found" errors possible
Version conflictsNone, you ship your versionPossible ("DLL hell", Section 7)
Security fixesYou must ship themArrive through Windows Update
PortabilityCopy one fileMust bring the right DLLs along

A visual: three programs, same library.

 STATIC                                  DYNAMIC

 ┌──────────┐ ┌──────────┐ ┌──────────┐  ┌────────┐ ┌────────┐ ┌────────┐
 │ App A    │ │ App B    │ │ App C    │  │ App A  │ │ App B  │ │ App C  │
 │ +lib copy│ │ +lib copy│ │ +lib copy│  └───┬────┘ └───┬────┘ └───┬────┘
 └──────────┘ └──────────┘ └──────────┘      └──────────┼──────────┘
  3 copies of the library                        ┌──────┴──────┐
                                                 │  lib.dll    │
                                                 └─────────────┘
                                                  1 shared copy

Real life on Windows: Most of the Windows API is dynamic. But many programs mix both. For example, a program may statically link a small helper library and dynamically use kernel32.dll. The Visual C++ runtime (the helper code every C/C++ program needs) can even be linked either way: dynamically, which requires the matching vcruntime DLLs to be installed, or statically, which makes a larger, self-contained EXE. (Hedge: the choice is a compiler setting.)

Windows-specific tip: Why do some programs come with an installer that adds "Microsoft Visual C++ Redistributable"? Because they were linked dynamically to the Visual C++ runtime DLLs. The installer makes sure those shared DLLs exist on your PC.

Which should you choose?

  • Small tool you give to a friend, no installation: static is easy.
  • Big program family sharing code: dynamic saves space and makes updates easy.
  • Code that must get urgent security fixes: dynamic lets the fix arrive without a new build.

Summary

TopicRemember this
Static linkingThe linker copies library code into the EXE at build time
Dynamic linkingThe EXE holds notes; Windows connects the DLL at run time
Import libraryA small .lib with names only, no code
Load-time vs run-timeLoad-time uses the import table; run-time uses LoadLibrary and GetProcAddress
Delay-loadConnect on first call, not at start
Trade-offStatic = self-contained but big; dynamic = small, shared, easy to update, but can break if a DLL is missing