C Preprocessor: Basics

The C Preprocessor: Why!

Dirty Hack!

  • Has nothing to do with the language itself

  • Invented to quickly solve problems in an ad hoc manner - completely ignoring the language

  • Brutal and stupid text replacement

  • ⟶ Please use cautiously!

#include

Include the content of a file at the point in the source file where #include is written

Two incarnations, with a subtle difference

  • #include "file.h": search first in the directory where the “includer” is, and then along the search path

  • #include <file.h>: searches only along the include path

No rules, but …

  • Header files contain declarations and macros

  • Header files generally include header files: have to protect themselves against multiple inclusioninclude guards

  • #include always comes near the beginning of a source file - never inside

Macros: Text Replacement

Macro: definition of a token that is brutally replaced

#define forever for (;;)

...
    forever {
        sleep(1);
        printf("No way out!");
    }

Macros: Constant Definition

#define LOWER 0
#define UPPER 300
#define STEP 20

for (i = LOWER; i < UPPER; i += STEP)
    ...

Better: C99 const keyword

const int LOWER = 0;

⟶ Typed immutable variable

Macros: Inline Replacement as Function Call (1)

Original problem

  • Function calls are slow

  • Parameter passing ⟶ copy

  • Return ⟶ copy

#define max(a, b) (((a) > (b))? (a) : (b))
...
x = max(1, 2);

⟶ Statement is expanded as if it were a function call

But …

Macros: Inline Replacement as Function Call (2)

Braces are necessary:

/* #define max(a, b) (((a) > (b))? (a) : (b)) */
#define max(a, b) ((a > b)? a : b)
...
x = max(p+q, r+s);

brutally expands to …

x = (p+q > r+s) ? p+q : r+s;

Operator precedence massacre!

Macros: Inline Replacement as Function Call (3)

C99: inline keyword

inline int max(int a, int b)
{
    return (a > b)? a : b;
}

Drawback: cannot use max() with different types

Macros: Inline Replacement as Function Call (4)

One more thing:

#define max(a, b) (((a) > (b))? (a) : (b))
...
x = max(i++, j++);

brutally expands to …

x = ((i++) > (j++)) ? (i++) : (j++);

Parameters are evaluated more than once!

Include Guards (1)

a.h
#include "c.h"
b.h
#include "c.h"
c.h
extern int g_lobal;
main.h
#include "a.h"
#include "b.h"

Error: multiple declaration of g_lobal

Include Guards (2)

Solution: define a “guard” macro by hand (!)

#ifndef HAVE_C_H
#define HAVE_C_H

extern int g_lobal;

#endif

OMG!

  • By hand - after all, it’s got nothing to do with C

  • ⟶ Bugs/errors are the logical consequence (e.g. guard macro clashes)

  • ⟶ GCC Extension: #include_once

The C Preprocessor: Last Words

  • C is low-level but not stupid

  • The C preprocessor is stupid

  • C programmers can take it

  • Newbies not

  • Unnecessary hurdle

  • Stupid!