█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Programming Languages/C/Compilation and Linking
50 minIntermediate

Compilation and Linking

After this lesson, you will be able to: Understand the preprocessor → compiler → linker pipeline; write Makefiles + headers with include guards.

Compilation isn't a single step. Understanding the phases unlocks debugging build errors.

Prerequisites:Standard Library

The four phases of compilation

1. Preprocessing (cpp): #include, #define, #ifdef substitution. Output: 'translation unit'. 2. Compilation (cc1): C → assembly. Output: .s file. 3. Assembly (as): assembly → machine code. Output: .o object file. 4. Linking (ld): combine .o files + libraries → executable. `gcc main.c -o main` runs all four. Use -E to stop at preprocessing, -S for assembly, -c for object only.

Headers + include guards

How C projects split across files.

tsx
// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H
int add(int a, int b);
int sub(int a, int b);
#endif
// math_utils.c
#include "math_utils.h"
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// main.c
#include <stdio.h>
#include "math_utils.h"
int main(void) {
printf("%d\n", add(2, 3));
return 0;
}
// Compile
// gcc -c math_utils.c -o math_utils.o
// gcc -c main.c -o main.o
// gcc main.o math_utils.o -o app

A real Makefile

Reproducible builds without typing commands.

bash
# Makefile
CC = gcc
CFLAGS = -Wall -Wextra -std=c17 -g
LDFLAGS = -lm
SRCS = main.c math_utils.c
OBJS = $(SRCS:.c=.o)
TARGET = app
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: all clean
# Use:
# make, build
# make clean, remove build artifacts
# Modern alternative: CMake (covered in C++ sub-track)

Linker errors

'undefined reference to X': you used X but didn't link the .o that defines it (or the library). 'multiple definition of X': X is defined in two translation units. Mark static (if local) or put in a single .c. Headers should declare; .c files should define.

Common mistakes only experienced C devs avoid

Missing include guards, multiple-include errors. Defining functions in headers (vs declaring). Multiple-definition linker errors. Forgetting -lm for math.h. Hand-typing gcc commands. Makefiles or CMake save real time on bigger projects.

Quick Check

What does an include guard prevent?

Pick the cleanest answer.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←The C Standard Library
Back to C
C and Security→