student@ubuntu:~$
Topic Week 5 2 min overview

Three-File Format

main.c + lib.c + lib.h, the one-definition rule, and why programs split cleanly or painfully

In a nutshell

In Java, when Main.java needed Car, the compiler found Car.class on the classpath and worked out the rest. In C you do that work by hand. A program with more than one source file uses the three-file pattern: main.c (drives the program), lib.c (holds function definitions), lib.h (holds function prototypes, #define constants, and extern declarations of shared data). Every .c file that needs a function declared in lib.h writes #include "lib.h", and the linker stitches the object files together at build time.

The one-definition rule is the non-negotiable: every function and every variable has exactly one definition in the whole program, even though it can be declared (through #include) as many times as needed. extern is how you declare without defining. Include guards (#ifndef/#define/#endif) prevent a header from being pasted into the same translation unit twice.

Why it matters

Every lab from Lab 6 onward has more than one .c file. Every real-world C project (the Linux kernel, git, sqlite, curl) uses exactly this pattern at massive scale. A program that is organized well splits naturally; one that is organized poorly produces multiple-definition linker errors, circular includes, and changes that cascade across the whole source tree. This is also where make pays for itself: with the three-file pattern, make can rebuild only the files whose sources (or header dependencies) actually changed.

Key takeaways

  • .h files declare. .c files define. Prototypes, #define, typedef, struct layouts (when shared), and extern declarations go in .h. Function bodies and variable definitions with storage go in exactly one .c file.
  • One-definition rule. For any name, many declarations are fine; exactly one definition is required.
  • extern declares; it does not define. extern const double FLAT_R; in a header, const double FLAT_R = 5.00; in exactly one .c file.
  • Include guards prevent double-inclusion within one translation unit. They do not protect against accidentally putting a definition in a header (which creates multiple definitions across translation units, not within them).
  • Use "..." for your own headers, <...> for system headers. Different preprocessor search rules.
  • make is a build tool, not a compile tool. It runs gcc commands you tell it to, and only when their inputs changed.

Lessons in this topic

Lesson What it covers
Three-File Model & extern Interface vs implementation, include guards with mechanism, declarations vs definitions, extern with a worked example, Makefile basics, CLI args

Planned for week 6 (coming):

  • make internals: .PHONY, pattern rules, dependency tracking with -MMD
  • static keyword (its three different meanings in C)
  • Header hygiene: forward declarations vs full definitions

Practice and deep dives

Practice this topic: Headers & Makefiles drill, or browse the practice gallery.

What comes next

Week 6 opens with Structs & typedef. Once you can organize code across files and bundle related data into struct types, you have the basic toolkit for every real C program.