By the end of this lesson, cadets will be able to:
#include with a project header file to access declarations from another translation unit. (Outcome 2)make to build a multi-file C program from provided source files. (Outcome 7)Complete before class. The MTBE sections introduce Makefile syntax; you will write one in the walkthrough.
| Source | Sections | Why |
|---|---|---|
| MTBE | Getting Started through Variables | The syntax of targets, dependencies, recipes, and variables |
| Beej-C | Chapter 4 review (Functions, prototypes) | Optional. Refresh prototypes; they appear in this lesson's headers. |
make uses file timestamps to decide what to rebuildCC and CFLAGSThis lesson sits on top of everything from Lessons 1 through 6, but the topic is new: how a real C project is built. The reference material below covers the concepts you will see in lecture and use in the Makefile walkthrough.
The Lesson 7 deck walks through multi-file programs, the four phases of compilation, Makefile syntax, variables, and the incremental rebuild story. The walkthrough in Section 5 is where you practice writing one.
Up to now you have written one .c file at a time. Real C projects are not one file. They split into many. The reasons are practical: a file with thousands of lines is hard to read; functions that belong together belong in their own file; teams of cadets (or engineers) cannot all edit the same file at once.
When code is split across files, three pieces of vocabulary appear.
| Piece | What it does |
|---|---|
| .c file (source) | Contains function definitions — the actual code that runs. |
| .h file (header) | Contains function declarations (prototypes) so other files can call those functions. |
| .o file (object) | The compiled output of one .c file. Not yet a runnable program; the linker glues several of them together. |
A typical file pair looks like this. greet.h declares what greet.c provides:
When another file wants to call make_greeting, it includes the header:
The include-form rule. Quotes for project headers (search the source directory first), angle brackets for library headers (search system include paths). Mixing these up gives you No such file or directory errors.
Lesson 20 will revisit multi-file design properly: include guards, what belongs in which file, the static keyword for file scope. For now the goal is to use a multi-file project well enough to build it with Make.
When you type gcc hello.c -o hello, gcc actually runs four steps under the hood. Knowing the names helps you read error messages.
| Phase | Input → Output | What happens |
|---|---|---|
| Preprocess | .c → .i | #include directives are replaced with the contents of header files. Macros expand. |
| Compile | .i → .s | C source becomes assembly language for your CPU. Most compile errors happen here (syntax, type mismatches). |
| Assemble | .s → .o | Assembly becomes machine code in an object file. Object files cannot run on their own; they are puzzle pieces. |
| Link | .o files → exe | One or more object files plus standard libraries get combined into an executable. undefined reference errors happen here. |
Why this matters for reading errors. If gcc says expected ';' before something, that is a compile error. If gcc says undefined reference to 'foo', that is a linker error. They have different causes and different fixes. The error-matching self-check in Section 5 trains you to tell them apart.
The -c flag tells gcc to stop after the assemble phase, producing a .o file. Without -c, gcc does the whole pipeline including linking, which is why a single gcc hello.c can produce a runnable program directly.
A Makefile is a file named exactly Makefile (capital M, no extension) that lives in your project directory. It contains rules. Each rule has three parts.
greet.o or program).A complete (tiny) Makefile that builds program from hello.c:
Run make with no arguments and it builds the first target listed. Run make clean to invoke a target named clean, which conventionally removes generated files.
Two variables appear in almost every C Makefile. They are conventional, not built into Make itself, so you have to define them yourself.
CC is the C compiler to use. CFLAGS is the list of flags to pass to it. To use the variable inside a recipe, write $(CC) or $(CFLAGS) (the dollar sign and parentheses are required).
CFLAGS must include -Wall and -Werror. -Wall turns on warnings; -Werror promotes warnings to errors so they cannot be ignored. The lab autograder checks for both.A project with 6 source files needs at least 7 gcc commands to build from scratch (one compile per .c, one link). Doing that by hand every time you change one line is tedious and error-prone. Worse, most of the work is wasted: only the file you changed actually needs to recompile.
make uses file timestamps to figure out the minimum work. For each rule, if any prerequisite is newer than the target, the recipe runs. Otherwise it is skipped. Walk the rules in dependency order and you get the shortest possible rebuild.
.c file, run make again.h file used by 3 modulesHeader dependencies matter. Make only knows about dependencies you list explicitly. If your rule says main.o: main.c but main.c #includes mathy.h, then changes to mathy.h will not trigger a rebuild of main.o. The result is a build that compiles fine but produces a broken executable, because main.o was generated from a stale view of mathy.h. List every header in every .o rule's prerequisites.
All of this becomes concrete in the Makefile walkthrough in Section 5.
The DFCS C Programming Standard is the course style guide. You have been following it implicitly since Lesson 1 (comment header block, line length, naming, brace placement). Starting this lesson, the formatting rules from Section 6 (Indentation, Delimiters, Whitespace) are enforced by tooling rather than by memory.
Two configuration files now ship with every CS210 lab template:
| File | What it does |
|---|---|
| .clang-format | Encodes the Section 6 rules: 4-space indentation, open brace on the same line as the statement, one space between operators and operands, 100-character line cap. Your editor reformats your code on save. |
| .editorconfig | Universal editor settings: spaces (not tabs) in .c and .h files, LF line endings, final newline at end of file. The Makefile gets tabs (Make needs them). |
Turn on Format on Save in your editor and the formatter takes over. You will not lose points on Section 6 rules from this lesson forward.
The content rules in the standard still apply: variable and function names (Section 5), no magic numbers or global variables or single-line ifs (Section 7), defensive programming (Section 8). No formatter can check those for you. See the Resources section for the full DFCS C Programming Standard.
Three things to do in class. Use the checklist to track your progress.
Ten tasks across four sections. Start by compiling and linking a 4-module project by hand. Then write a Makefile that automates the build, refactor it with variables, and watch incremental rebuilds save work.
Enter your name. It will appear on the completion screen so you can include it in your screenshot for the Lesson 7 reflection.
Five scenarios. Each shows code or a Makefile snippet, the command that ran, and the error you got back. Pick the cause. Reading errors and matching them to causes is a learnable skill.
Enter your name. It will appear on the score screen so you can include it in your screenshot for the Lesson 7 reflection.