Move from a single .c file to a realistic header/source/Makefile layout.
// declarations only #ifndef MATHUTIL_H #define MATHUTIL_H int add(int a, int b); int mul(int a, int b); int factorial(int n); #endif
#include "mathutil.h" int add(int a, int b) { return a + b; } int mul(int a, int b) { return a * b; } int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); }
#include <stdio.h> #include "mathutil.h" int main(void) { printf("2+3 = %d\n", add(2, 3)); printf("4*5 = %d\n", mul(4, 5)); printf("5! = %d\n", factorial(5)); return 0; }
.h lists "what this module exports" (declarations). The .c is "how it actually works" (implementation). Readers should be able to use the module from the header alone.main.c changes, recompile just it and relink. That's what Make automates.| Flag | Meaning |
|---|---|
-c | Compile only (no linking). Produces .o |
-o name | Output file name |
-Wall -Wextra | Enable common warnings. Always use these. |
-g | Include debug info (for gdb) |
-O0 / -O2 | No optimization / normal release level |
-std=c11 / -std=c17 | Select the C standard version |
-I dir | Add a header search path |
-L dir | Add a library search path |
-l name | Link libname (e.g. -lm for math) |
-D MACRO | Define a preprocessor macro |
// top and bottom of mathutil.h #ifndef MATHUTIL_H #define MATHUTIL_H /* header contents */ #endif
#pragma once β a single line that does the same thing. Not in the C standard, but widely portable.#ifndef CONFIG_H #define CONFIG_H extern int g_verbose; // declaration only #endif
#include "config.h" int g_verbose = 0; // the one and only definition
int g_verbose; in the header: every .c that includes it would get its own definition β "multiple definition" linker error.make tool reads a Makefile, detects which files changed, and rebuilds only what is needed.# Makefile β filename must start with capital M # IMPORTANT: lines under a target must start with a TAB character app: main.o mathutil.o gcc main.o mathutil.o -o app main.o: main.c mathutil.h gcc -c main.c mathutil.o: mathutil.c mathutil.h gcc -c mathutil.c clean: rm -f *.o app
CC = gcc
CFLAGS = -Wall -Wextra -O2 -g
OBJS = main.o mathutil.o
TARGET = app
$(TARGET): $(OBJS)
$(CC) $(OBJS) -o $(TARGET)
# implicit rule handles .c β .o automatically
main.o: main.c mathutil.h
mathutil.o: mathutil.c mathutil.h
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: clean
clean on disk would make make think the target is already done.CFLAGS += -MMD -include $(OBJS:.o=.d)
-MMD flag writes .d files with header dependencies, so make rebuilds whenever any included header changes.make, then ./app.sqrt() from <math.h>. Add -lm to LDFLAGS and make sure it links.make debug uses -g -O0, make release uses -O2.