Preprocessor Ready
main.c

                

Ready

Statements run 0 Lines printed 0
Ready

Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode

How it works

The ideas behind the lab

Exactly what you watched happen, in plain steps.

From source file to program

Compiling starts with the preprocessor. It handles every line that begins with #: #include copies a header file in, #define creates macros that are replaced as plain text, and #if / #ifdef keep or drop blocks of code. Only the result goes to the compiler. When the finished program runs, main can receive the command line it was started with: argc is the number of words and argv an array of strings holding them.

#include <stdio.h>          // paste in a header
#define MAX 100              // a constant: MAX → 100
#define SQUARE(x) ((x) * (x))   // a macro with a parameter

#ifdef DEBUG                 // only if DEBUG is defined
    printf("x = %d\n", x);
#endif

int main(int argc, char *argv[])   // $ ./prog a b → argc = 3
Preprocessor directives
Directive What it does Example
#include <file> paste a system header #include <stdio.h>
#include "file" paste your own header (searched first in your folder) #include "shapes.h"
#define NAME text replace NAME by text everywhere after it #define SIZE 10
#define F(x) text a macro with parameters #define MAX(a, b) ((a) > (b) ? (a) : (b))
#undef NAME forget a macro #undef SIZE
#if / #ifdef / #ifndef / #else / #endif conditional compilation #ifdef DEBUG … #endif

Macros

  1. #define replaces text before compiling — no type, no memory, no checks.
  2. Put brackets around every parameter and around the whole macro body.
  3. Never pass expressions with side effects: SQUARE(i++) increments twice.
  4. No semicolon at the end of a #define (it would be pasted in too).
  5. For typed constants, const int or enum is often the better choice.

Headers and conditional compilation

  1. A header (.h) holds prototypes, types and macros shared by several .c files.
  2. #ifdef NAME … #endif keeps the code only if NAME is defined.
  3. gcc -DDEBUG prog.c defines DEBUG from the command line.
  4. Header guards stop a header being included twice: #ifndef SHAPES_H / #define SHAPES_H / … / #endif.
  5. gcc -E prog.c shows the code after preprocessing.

argc and argv

  1. int main(int argc, char *argv[]) receives the command line.
  2. argv[0] is the program name; argv[1] … argv[argc − 1] are the arguments.
  3. argv[argc] is NULL.
  4. Every argument is a string: convert with atoi, atof or strtol.
  5. Check argc before using argv[i], and print a usage message if something is missing.
Walkthrough

The largest of the numbers given on the command line

Every line, explained.

1 #include <stdio.h>
2 #include <stdlib.h> For atoi.
3
4 #define MAX_ARGS 10
5 #define MAX(a, b) ((a) > (b) ? (a) : (b)) Brackets everywhere.
6
7 int main(int argc, char *argv[]) Run as: ./max 4 17 9
8 {
9 if (argc < 2) { No numbers given?
10 printf("usage: %s numbers...\n", argv[0]);
11 return 1;
12 }
13 int best = atoi(argv[1]); "4" → 4
14 for (int i = 2; i < argc && i <= MAX_ARGS; i++)
15 best = MAX(best, atoi(argv[i])); Expands to ((best) > (atoi(argv[i])) ? … )
16 #ifdef DEBUG Compiled only with gcc -DDEBUG.
17 printf("checked %d arguments\n", argc - 1);
18 #endif
19 printf("largest: %d\n", best); Prints largest: 17
20 return 0;
21 }
Common mistakes

Spot the bug

Mistakes almost every beginner makes — and how to fix them.

Macro without brackets

✗ Wrong
#define SQUARE(x) x * x
SQUARE(a + 1)   // a + 1 * a + 1
✓ Right
#define SQUARE(x) ((x) * (x))
SQUARE(a + 1)   // ((a + 1) * (a + 1))

Macros paste text; operator precedence then splits the argument.

A semicolon after #define

✗ Wrong
#define SIZE 10;
int a[SIZE];   // int a[10;];
✓ Right
#define SIZE 10
int a[SIZE];

Everything after the name is pasted, the semicolon included.

Side effects in macro arguments

✗ Wrong
int y = SQUARE(i++);   // i is incremented twice
✓ Right
int y = SQUARE(i);
i++;

The argument appears twice in the expansion, so it is evaluated twice.

Using argv without checking argc

✗ Wrong
int n = atoi(argv[1]);   // crash if no argument
✓ Right
if (argc < 2) { printf("usage: …\n"); return 1; }
int n = atoi(argv[1]);

argv[1] does not exist (it is NULL) when the program is started without arguments.

Treating an argument as a number

✗ Wrong
int n = argv[1];      // an address, not 42
✓ Right
int n = atoi(argv[1]);

Every argument is a string; convert it first.

Practice

Check your understanding

Answer each question — you will see the explanation straight away.

0 / 5 correct
Question 1

When does the preprocessor run?

Question 2

#define DOUBLE(x) x + x — what is 3 * DOUBLE(2)?

Question 3

The program is run as ./prog red blue. What is argc?

Question 4

The program is run as ./prog red blue. What is argv[1]?

Question 5

What is the purpose of #ifndef X_H / #define X_H / … / #endif in a header?

Assessment

Preprocessor & Command-Line Arguments quiz

Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.