Skip to content

Intermediate · The preprocessor, multi-file programs and make · topic 38 of 60 · 25 min

Splitting a program into files

Explanation

Real C programs are split into modules: a header (.h) declares what a module offers (types, function prototypes, extern variables), and a source file (.c) defines it. Each .c file is compiled separately into an object file (gcc -c), and the linker joins them. "undefined reference" means something was declared but never defined (or not linked); "multiple definition" means it was defined in more than one file.

Why split?

A single 20,000-line file is hard to read, slow to recompile, and impossible for a team to edit without conflicts. Splitting by responsibility (tax.c, invoice.c, storage.c) lets each part be understood, tested and rebuilt on its own.

A header and its source file

c
/* tax.h: the interface */
#ifndef TAX_H
#define TAX_H

double gst_amount(double price, int rate_percent);
extern int default_gst_rate;           /* declared here, defined in tax.c */

#endif
c
/* tax.c: the implementation */
#include "tax.h"

int default_gst_rate = 18;             /* the one definition */

static double round2(double x) {       /* private to tax.c (static) */
    return (long)(x * 100 + 0.5) / 100.0;
}

double gst_amount(double price, int rate_percent) {
    return round2(price * rate_percent / 100.0);
}
c
/* main.c: a user of the module */
#include <stdio.h>
#include "tax.h"                       /* "" searches the project folder first; <> the system folders */

int main(void) {
    printf("GST: %.2f\n", gst_amount(1499.0, default_gst_rate));   /* GST: 269.82 */
    return 0;
}
bash
gcc -std=c17 -Wall -c tax.c          # → tax.o
gcc -std=c17 -Wall -c main.c         # → main.o
gcc tax.o main.o -o billing          # link
# or in one step: gcc -std=c17 -Wall tax.c main.c -o billing

What goes where

In the header (.h)In the source (.c)
Include guardFunction bodies
Type definitions (struct, typedef, enum) others needGlobal variable definitions (int x = 0;)
Function prototypesstatic helper functions and variables
extern declarations of shared globals#include of its own header first
Macros and static inline functions others need

Reading linker errors

output
/usr/bin/ld: main.o: in function 'main':
main.c:(.text+0x1a): undefined reference to 'gst_amount'
collect2: error: ld returned 1 exit status
Declared (the prototype was seen) but not defined in any linked file: you forgot to compile/link tax.c, misspelled the name, or the definition is static.
output
/usr/bin/ld: tax.o:(.data+0x0): multiple definition of 'default_gst_rate'; main.o:(.data+0x0): first defined here
A global was DEFINED in a header (int default_gst_rate = 18; in tax.h), so every .c including it defined its own copy. Put only extern declarations in headers.
Careful: Never define functions or global variables (with storage) in a header that several .c files include; it causes multiple-definition link errors (or, with some old compiler settings, silent sharing). Headers declare; source files define. The exception is static inline functions, which are designed to live in headers.
Think of it like this: A header is like a restaurant menu: it lists what dishes exist and what you get. The .c file is the kitchen where the dishes are actually cooked. Customers (other files) only need the menu; if a dish is on the menu but no kitchen cooks it, the order fails ("undefined reference").
Tip: Each .c file should #include its own header first. If the header is missing an #include it needs, this makes the compiler tell you immediately.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Splitting a program into files · 1/6

Multi-file programs

In this lesson you will split a C program into several files.

Prefer a video by a teacher? Find free videos on “c multiple source files header files extern linker errors undefined reference” on YouTube ↗

In real life: Organising a clinic billing program

A clinic's billing tool grew from one file into modules: patient.c/h (patient records), tax.c/h (GST on services), invoice.c/h (building invoices) and main.c (menus). When the GST rules changed, only tax.c was edited and recompiled, and the rest of the program relinked in seconds.

output
billing/
├── main.c
├── patient.h   patient.c
├── tax.h       tax.c
└── invoice.h   invoice.c        (invoice.c includes patient.h and tax.h)

Practice (0/3 done)

Task 1 · Question

You compile with gcc main.c -o app and get "undefined reference to `gst_amount'". The function is in tax.c. What is the fix?

Task 2 · Question

config.h contains int max_users = 100; and is included by three .c files. What happens at link time?

Task 3 · Practice

Split a small program into money.h/money.c (a Money type in paise with add, multiply-by-quantity and format functions), cart.h/cart.c (adding items to a cart and computing the total using money.h) and main.c. Use include guards, keep helpers static, compile each file with -c, and link. Then deliberately cause and fix an "undefined reference" and a "multiple definition" error.

Quick quiz

Question 1

1.What does gcc -c file.c produce?

Question 2

2.What does extern int count; in a header mean?

Question 3

3.What is the difference between #include "x.h" and #include <x.h>?

Question 4

4.Which belongs in a header?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does gcc -c file.c produce?

Answer: An object file (file.o) that is not yet linked

Compile only, no linking.

What does extern int count; in a header mean?

Answer: count is defined in some .c file; this just declares it

A declaration without storage.

What is the difference between #include "x.h" and #include <x.h>?

Answer: "" searches the project's folders first; <> searches the system include folders

Use "" for your own headers.

Which belongs in a header?

Answer: A function prototype

Prototypes and types.

Which include style is used for your own header file?Related

Answer: #include "myfile.h"

Quotes search your project folder first; angle brackets are for standard and system headers.

From Functions: declare, define, call and return

What does extern int count; mean?Related

Answer: count is defined somewhere else (another file)

A declaration, not a definition.

From Scope and storage classes

What is a prototype for?Related

Answer: To declare the function's signature before it is used

The compiler can then check calls.

From Functions

Which stage expands #include and #define?Related

Answer: Preprocessing

gcc -E shows the preprocessor output.

From Compiling with gcc

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “The preprocessor, multi-file programs and make” before its exam: Building with make, Libraries: static and shared.
  2. Module 8 exam: Preprocessor, multi-file programs and make · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Bits, undefined behaviour and safety. 60 topics left in the course before the final exam.

← Previous: Conditional compilation and include guards