Structures, Files & More
Preprocessor, Macros & Command-Line Arguments
What happens before compilation: #include, #define macros and their traps, conditional compilation, header guards, and reading arguments with argc/argv.
Before the compiler sees your code, the preprocessor runs. It handles every line starting with #. It does pure text substitution — it does not understand C types or syntax.
#include
#include <stdio.h> // angle brackets: search the system include folders
#include "mylib.h" // quotes: search your project folder first#define: constants and macros
#include <stdio.h>
#define PI 3.14159
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main(void) {
printf("Area of r=2: %.2f\n", PI * SQUARE(2));
printf("MAX(7, 12) = %d\n", MAX(7, 12));
printf("SQUARE(3 + 1) = %d\n", SQUARE(3 + 1));
return 0;
}Area of r=2: 12.57 MAX(7, 12) = 12 SQUARE(3 + 1) = 16
The bracket trap
Because macros are text replacement, missing brackets give wrong answers:
#include <stdio.h>
#define BAD_SQUARE(x) x * x
#define GOOD_SQUARE(x) ((x) * (x))
int main(void) {
printf("BAD_SQUARE(3 + 1) = %d\n", BAD_SQUARE(3 + 1)); // 3 + 1 * 3 + 1 = 7
printf("GOOD_SQUARE(3 + 1) = %d\n", GOOD_SQUARE(3 + 1)); // (3 + 1) * (3 + 1) = 16
printf("100 / BAD_SQUARE(5) = %d\n", 100 / BAD_SQUARE(5)); // 100 / 5 * 5 = 100
printf("100 / GOOD_SQUARE(5) = %d\n", 100 / GOOD_SQUARE(5));
return 0;
}BAD_SQUARE(3 + 1) = 7 GOOD_SQUARE(3 + 1) = 16 100 / BAD_SQUARE(5) = 100 100 / GOOD_SQUARE(5) = 4
Predefined macros
#include <stdio.h>
int main(void) {
printf("File: %s, line: %d\n", __FILE__, __LINE__);
printf("Compiled on %s at %s\n", __DATE__, __TIME__);
printf("C standard version: %ld\n", __STDC_VERSION__);
return 0;
}File: main.c, line: 4 Compiled on Oct 31 2026 at 13:03:21 C standard version: 201710
Conditional compilation
Include or exclude code before compiling — useful for debug output and for code that differs between Windows and Linux:
#include <stdio.h>
#define DEBUG 1
int main(void) {
int total = 0;
for (int i = 1; i <= 4; i++) {
total += i * i;
#if DEBUG
printf("[debug] i = %d, total = %d\n", i, total);
#endif
}
printf("Sum of squares: %d\n", total);
#ifdef _WIN32
printf("Built for Windows\n");
#else
printf("Built for Linux or macOS\n");
#endif
return 0;
}[debug] i = 1, total = 1 [debug] i = 2, total = 5 [debug] i = 3, total = 14 [debug] i = 4, total = 30 Sum of squares: 30 Built for Linux or macOS
Header guards
When you split code into files, put declarations in a header. Wrap the header in a guard so including it twice does no harm:
// mathutil.h
#ifndef MATHUTIL_H
#define MATHUTIL_H
int gcd(int a, int b);
int lcm(int a, int b);
#endifCommand-line arguments
main can receive the words typed after the program name:
argc— argument count, including the program name.argv— argument vector: an array of strings.argv[0]is the program name.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 3) {
printf("Usage: %s num1 num2\n", argv[0]);
return 1;
}
int a = atoi(argv[1]), b = atoi(argv[2]); // convert text to int
printf("%d + %d = %d\n", a, b, a + b);
return 0;
}gcc add.c -o add
./add 12 30
# 12 + 30 = 42Test yourself
4 questions · pick an answer to check it instantly
- 1
What does
#define TRIPLE(x) x * 3give forTRIPLE(2 + 1)?Text substitution gives2 + 1 * 3= 2 + 3 = 5. Bracket macro parameters:((x) * 3). - 2
What is
argv[0]?User arguments start atargv[1];argccounts all of them including the program name. - 3
What are header guards for?
#ifndef X / #define X / ... / #endifmakes repeated includes harmless. - 4
When do preprocessor directives run?
The preprocessor rewrites the source text before the compiler proper starts.