Skip to content
Lesson 13 of 13

Step 1 of 7 · Reading · ~3 min

Learn

File I/O and the Preprocessor

The Preprocessor

Before the compiler sees a single line of your C, a separate program has already rewritten it. The preprocessor obeys every line that begins with # — pasting files in, substituting text, deleting whole regions — and hands the result to the compiler, which never learns any of it happened. Everything strange about macros follows from one fact: this stage does text, not C. It knows nothing about your types, your scopes or your values.

#include pastes a file

#include <stdio.h>      /* angle brackets: search the system include paths */
#include "grid.h"       /* quotes: look next to this source file first */

Literally pastes. <stdio.h> is a few thousand lines of declarations, and after this stage your file contains them — which is how the compiler knows the shape of printf before you call it, and why C needs no import machinery of its own.

#define substitutes text

#define MAX_ROWS 64
#define AREA(w, h) ((w) * (h))

int grid[MAX_ROWS];         /* becomes: int grid[64];            */
int a = AREA(3, 2 + 2);     /* becomes: ((3) * (2 + 2))  ->  12  */

An object-like macro is a name swapped for text. A function-like macro swaps its arguments in as text too, and that is where the parentheses earn their keep:

#define BAD(x) x*x*x

int n = 2;
BAD(n+1)      /* expands to  n+1*n+1*n+1  ->  7, not 27 */

Nothing multiplied n+1 by itself. The characters n+1 were dropped into three slots and * binds tighter than +. So wrap every use of a parameter and the whole body: #define CUBE(x) ((x) * (x) * (x)).

Two more consequences of "it is only text". A macro is not a statement, so a trailing semicolon rides along into every expansion — #define CUBE(x) ((x)*(x)*(x)); makes printf("%d", CUBE(n)); fail to compile with expected ')' before ';' token. And because arguments are pasted rather than evaluated, AREA(i++, 2) increments i twice.

Prefer const and enum for plain constants

const int max_rows = 64;      /* typed, scoped, visible in a debugger */
enum { MAX_ROWS = 64 };       /* integer constants, also typed */

Both live inside the C type system. #define does not, so a mistake in a macro is reported against the expanded text at the point of use, often pointing at a line you did not write. Keep macros for the jobs only text substitution can do: function-like macros and conditional compilation.

Conditional compilation and header guards

#ifdef DEBUG                  /* switched on by -DDEBUG on the gcc line */
    fprintf(stderr, "n = %d at %s:%d\n", n, __FILE__, __LINE__);
#endif

#ifdef, #ifndef and #if defined(...) keep or delete whole regions before compilation — the standard way to carry debug-only code and platform-specific code in one source file. (__FILE__ and __LINE__ are macros the compiler defines for you, along with __func__ for the current function name.)

The same mechanism guards headers. A header that another header also includes gets pasted twice into one translation unit, and everything it declares is redefined:

#ifndef GRID_H
#define GRID_H
/* declarations */
#endif

The first inclusion defines GRID_H and keeps the body; every later one finds it defined and skips to #endif. #pragma once does the same job in one line on every compiler you are likely to meet, but it is not in the standard, which is why guards still win in portable code.

Your exercise

Define CUBE(x) and print N cubed is <C>. Write the fully parenthesised form — and know exactly what this exercise can check: the grader only ever hands CUBE a plain variable, and n*n*n is correct for a plain variable, so the careless version passes here too. The parentheses are the habit that pays the day someone writes CUBE(n+1).

What the grader does catch is real enough: a macro with a trailing semicolon will not compile, %d needs an int on the other side of it, and the wording is matched exactly — lowercase cubed is, single spaces.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…