devnotes.

Header Files and Multi-File Projects

Real C projects split code across multiple .c files, sharing declarations through .h header files rather than putting everything in one giant file.

// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int add(int a, int b);       // declaration only — no body
int multiply(int a, int b);

#endif
// math_utils.c
#include "math_utils.h"

int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
// main.c
#include <stdio.h>
#include "math_utils.h"      // quotes for local headers, <> for system headers

int main(void) {
    printf("%d\n", add(2, 3));
    return 0;
}

The header declares what functions exist (their prototypes); the corresponding .c file defines how they work. Any other file that wants to call add or multiply just #includes the header, without needing to see or recompile the implementation. Angle brackets (<stdio.h>) tell the compiler to search standard system directories; quotes ("math_utils.h") tell it to look in the local project directory first.

Compiling a multi-file project means compiling each .c file and linking the resulting object files together:

gcc -c math_utils.c -o math_utils.o
gcc -c main.c -o main.o
gcc math_utils.o main.o -o app

For anything beyond a couple of files, a Makefile automates this so you don't retype these commands by hand, and only rebuilds files that actually changed since the last build — most real C projects use make or a more modern build system (CMake, Meson) for exactly this reason.

Header Files and Multi-File C Projects | DevNotes