P-3.5 Compiling, Linking and Build Tools
Standard C toolchain behaviour — written September 2026
What this is and why it exists
Every language hides a build system. C's is thin enough to see straight through, which is the reason to look at it here.
There are four separate steps between your source files and a program you can run. Each one can fail on its own terms, and each produces a different kind of complaint. Being able to name which step failed cuts most build problems roughly in half.
One distinction saves more time than all the others. A compile error is about *one file*. A link error is about *how the files fit together*.
The vocabulary
- Preprocessor — the step that pastes text, before any compiling happens.
- Header — a file of declarations, included by pasting its text in.
- Declaration — a promise that something exists somewhere.
- Definition — the thing itself.
- Object file — one compiled source file, not yet a program.
- Linker — the step that joins object files and libraries into an executable.
- Static library — library code copied into your executable.
- Dynamic library — library code loaded when the program starts.
The mental model
The four stages are preprocess, compile, assemble, link. Each takes the previous stage's output. Running them separately once, by hand, turns the whole process from something magical into something ordinary, and it takes about ten minutes.
The preprocessor only pastes text. That is worth repeating, because it explains a whole class of confusing message. Including a header copies its text into your file before compiling begins. So a missing *declaration* and a missing *definition* are different complaints from different stages. One is the compiler saying it has never heard of this name. The other is the linker saying it believed you and could not find the thing.
Which is what "undefined reference" means. The compiler was satisfied that a function exists somewhere, because a declaration promised it. The linker then went looking and did not find it. Reading that message as a spelling problem, when it is usually a library that was not linked, wastes a great deal of time.
Libraries come in two kinds and the choice has consequences. Static means the code is copied into your executable: a bigger file that runs anywhere. Dynamic means it is loaded at startup: a smaller file that needs the library present on the machine it runs on. Neither is correct in general, and the failure mode of the second is a program that runs on your machine and not on the server.
Finally, build tools. A build file records which outputs depend on which inputs, so only the affected parts are rebuilt after a change. Almost all confusing build behaviour comes from a dependency nobody declared. A change that seems not to take effect, or a rebuild that does far too much.
What you should now be able to explain or do
Name the four stages and say what each takes and produces. Run them separately once. Explain why the preprocessor only pasting text makes a missing declaration and a missing definition different problems. Read "undefined reference" as a link error and look for the library. Say what static and dynamic linking each cost you. Explain what a build tool is deciding, and where confusing build behaviour comes from.
Check yourself
What is the difference between a compile error and a link error?
A compile error is about one file on its own. A link error is about how the separately compiled files and libraries fit together.
What does the preprocessor actually do?
It pastes text. Including a header copies its contents into your file before compiling begins.
What does "undefined reference" usually mean?
A declaration promised a function exists and the linker could not find it. Most often a library was not linked, rather than a name misspelt.
What do you give up by linking dynamically?
The guarantee that it runs anywhere. The library has to be present on the target machine, which is a common deployment surprise.
Where does confusing build behaviour come from?
A dependency nobody declared. The tool rebuilds from what it was told, so an undeclared input produces a stale output.
Go deeper
We haven't checked most of these for screen reader use yet.
Back to Compiling, Linking and Build Tools: work through the checklist