By the end of this lesson, cadets will be able to:
#include, #define, conditional compilation. (Outcome 4)static at file scope to limit visibility of helpers. (Outcomes 2, 4)tests.c. (Outcomes 3, 7)Complete before class. These give you the vocabulary we will use in lecture.
| Source | Sections | Why |
|---|---|---|
| Beej-C | Chapter 17 (Multifile Projects) sections 1 through 4 | The canonical practical walkthrough |
| Beej-C | Chapter 19 (The C Preprocessor) sections 1 through 3 | #include, #define, and include guards |
| King, C Programming: A Modern Approach | Chapter 15 (Writing Large Programs) | Optional; deeper treatment of file organization |
main.c has more than two helper functions. On paper, sketch how you would split that program into a main.c plus a header and a second .c. Which functions go where? Which become static? You will revise your answer in class.Lesson 20 slides (PDF) · downloadable copy of the in-class deck. The slides are the spine of the in-class lecture; the header file walkthrough is where you practice the moves hands-on.
All semester you have been writing one big main.c. That works for small programs. Past a few hundred lines, it starts to hurt: search slows down, recompiles get slower, you can't reuse helpers, two cadets cannot work on the same file at once.
The fix is to split. But splitting is not a mechanical chore. Every split is a design decision: which file does this function belong in? What does the header expose, and what stays private? When you make those decisions well, the next person who reads your code (which is often you, six weeks from now) finds what they need immediately.
Today's content is the rules the preprocessor and linker enforce so that splitting actually works, and the design judgment you need to apply on top of those rules.
This is functional decomposition, the same skill you have been practicing since Lesson 6, one level up. At L6 you decided which lines of main should become a helper function. Today you decide which helper functions belong in which file. The judgment is the same; the granularity is bigger.
When you get the rules wrong, the linker (or compiler) tells you. There are basically four patterns to recognize:
undefined reference — the linker can't find a function's definitionmultiple definition — the linker found too many definitionsimplicit declaration — the compiler reached a call site with no prototypeconflicting types — the prototype and the definition disagreeEach of these has a different fix, and learning to map error → cause is the diagnostic skill we are after today. The linker error reference card walks through all four patterns with worked examples. Keep it open while you work the lab.
| declaration | A promise: "a function (or variable) named X exists somewhere with this signature." Lives in headers. |
| definition | The body. Lives in a .c. There must be exactly one definition per non-static function. |
| translation unit | One .c file plus every header it includes, fully expanded by the preprocessor. The compiler eats one of these at a time. |
| linker | The program that runs after the compiler and stitches your .o files together into one executable. Most multi-file errors come from the linker, not the compiler. |
| include guard | An #ifndef / #define / #endif wrapper around a header that prevents it from being pasted twice into the same translation unit. |
| static (at file scope) | Marks a function as private to its .c. The linker will refuse to let other files call it. |
Today's lab is the first lesson where you write your own test cases, not just consume the autograder's. Your tests.c file lives alongside main.c and your stats module and builds as its own executable. Each test you write needs a one-line comment naming the edge case it covers and why it matters.
We start the habit here in L20; Lesson 26 (Test Planning and Unit Testing) teaches it formally. The goal today is not perfect coverage; it is the shift from "the autograder tests me" to "I test my code."
Today's lesson is the first contact with five sections of the DFCS C Programming Standard. These rules apply to your code from this lesson forward.
| §1.2 | Header file structure: comment block, then guard, then includes, then defines, then typedefs, then prototypes. |
| §4.1 | Every header file has an include guard. Macro name is the filename in CAPS with underscores for dots. |
| §4.2 | Include only what you use. Reference a symbol, include the header that declares it directly. No transitive includes. |
| §2.1 | Comment header block on every file, including .h files. |
| §2.2 | Function header blocks in .h files: each prototype gets @brief, @param, @return comments directly above it. |
The full standard is in the Resources section on Blackboard. The lab autograder does not mechanically check these sections, but they are enforced for the rest of the course and surface on every quiz and GR from this point.
A nine-task walkthrough where you organize a small multi-file project. Each command edits the project tree on the right; the errors panel below shows what the linker would say about your current layout. By the end, the errors panel should be clean.
Enter your name. It will appear on the completion screen so you can include it in your screenshot for the Blackboard reflection.
static. The errors panel on the right shows what the linker would say about your current layout.Six error blocks. For each, identify the most likely cause. Pass at 70% or above. The completion code goes in your Lesson 20 reflection.
Enter your name. It will appear on the score card so you can include it in your screenshot for the Blackboard reflection.
provided.h and README.md end to end before writing any code.main.c, what goes in your own .c, and which header declares what?make and confirm zero warnings, then run both binaries before you push.