C Programming 3 min read

Headers and multi-file projects

A header declares what a module offers; its .c file holds the one definition of each item. Every .c file is compiled on its own, and the linker joins the objects. Include guards stop a header being pasted twice, extern shares a variable, and static keeps the rest private.

led.h holds the declarations. led.c and main.c both include it. Each .c file is compiled separately with gcc -c into led.o and main.o. The linker joins the object files, matching each use to its one definition, into firmware.elf.
Both files include led.h; each is compiled alone, and the linker joins them into one.

Declaration or definition

A declaration tells the compiler that a name exists and what type it has: void led_set(int on); or extern int led_state;. A definition also creates it: the function body, or the variable's storage. A name can be declared many times but defined only once in the whole program. So the header, led.h, holds declarations, types and macros, and led.c holds the definitions.

modules.cC
#include <stdio.h>/* ---- led.h: the interface other files include ---- */#ifndef LED_H#define LED_Hvoid led_set(int on);             /* declarations */int  led_changes(void);extern int led_state;#endif/* ---- led.c: exactly one definition of each ---- */int led_state;                    /* definition */static int changes;               /* private to led.c */void led_set(int on) {    if (on != led_state) {        changes++;    }    led_state = on;}int led_changes(void) {    return changes;}/* ---- main.c: uses only what led.h declares ---- */int main(void) {    led_set(1);    led_set(1);    led_set(0);    printf("state %d, changes %d\n", led_state, led_changes());    return 0;}
Output
state 0, changes 2

The example is shown as one file so that it compiles here; in a project, the three marked parts are led.h, led.c and main.c, and main.c starts with #include "led.h".

Guards, extern and static

The #ifndef LED_H / #define LED_H / #endif lines are an include guard: if a header is included twice, directly or through another header, the second copy is skipped. extern in the header lets other files use led_state, while static on changes keeps it private to led.c, so no other module can change it or clash with its name.