Lesson 11 of 12
The Preprocessor & Multi-File Programs
Learn what the preprocessor does before compilation, and how to split a program across several files using headers.
/* mathutils.h — the INTERFACE: types, declarations, macros */
#ifndef MATHUTILS_H /* include guard: prevents double inclusion */
#define MATHUTILS_H
#include <stddef.h>
#define PI 3.14159265358979323846
#define MAX(a, b) ((a) > (b) ? (a) : (b)) /* parenthesise every argument! */
#define ARRAY_LEN(a) (sizeof (a) / sizeof (a)[0])
typedef struct {
double x, y;
} Vec2;
double vec2_length(Vec2 v);
Vec2 vec2_add(Vec2 a, Vec2 b);
int clamp(int value, int low, int high);
#endif /* MATHUTILS_H *//* mathutils.c — the IMPLEMENTATION */
#include "mathutils.h" /* quotes: your own headers. <> : system headers */
#include <math.h>
double vec2_length(Vec2 v) {
return sqrt(v.x * v.x + v.y * v.y);
}
Vec2 vec2_add(Vec2 a, Vec2 b) {
return (Vec2){ a.x + b.x, a.y + b.y }; /* compound literal */
}
int clamp(int value, int low, int high) {
if (value < low) return low;
if (value > high) return high;
return value;
}
/* static = internal linkage: visible only inside this file */
static int helper_nobody_else_sees(int n) { return n * 2; }
/* main.c */
#include <stdio.h>
#include "mathutils.h"
int main(void) {
Vec2 a = {3, 4};
printf("%.1f\n", vec2_length(a)); /* 5.0 */
printf("%d\n", MAX(10, 3));
printf("%d\n", clamp(120, 0, 100)); /* 100 */
return 0;
}# Compile each .c to an object file, then link them together
gcc -Wall -Wextra -std=c17 -c mathutils.c -o mathutils.o
gcc -Wall -Wextra -std=c17 -c main.c -o main.o
gcc mathutils.o main.o -o app -lm # -lm links the math library
# Or in one step
gcc -Wall -Wextra -std=c17 main.c mathutils.c -o app -lm
# A minimal Makefile
# CC = gcc
# CFLAGS = -Wall -Wextra -std=c17 -g
# app: main.o mathutils.o
# $(CC) $^ -o $@ -lm
# %.o: %.c mathutils.h
# $(CC) $(CFLAGS) -c $< -o $@
# clean:
# rm -f *.o app
# See exactly what the preprocessor produced
gcc -E main.c | less#include <stdio.h>
#include <assert.h>
/* Conditional compilation */
#define DEBUG 1
#if DEBUG
#define LOG(fmt, ...) fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#else
#define LOG(fmt, ...) ((void)0)
#endif
/* Platform differences */
#ifdef _WIN32
#define PATH_SEP '\\'
#else
#define PATH_SEP '/'
#endif
/* Predefined macros: __FILE__ __LINE__ __func__ __DATE__ __STDC_VERSION__ */
int divide(int a, int b) {
assert(b != 0 && "divide by zero"); /* removed entirely when NDEBUG is defined */
return a / b;
}
int main(void) {
LOG("starting with sep %c", PATH_SEP);
printf("%d\n", divide(10, 2));
return 0;
}Finished reading?Mark it done to track your progress.
Tip: use the ← and → keys to move between lessons.