# Header Files & the Preprocessor

Every real C program you've seen starts with a line like `#include <stdio.h>`. You've been typing it since
Phase 1 without asking what it means. Now that you can write functions (Phase 4) and structs (Phase 7),
it's time to answer that question properly - because the moment your program grows past one file, you
can't avoid understanding it.

**The mental model first, before any syntax:** C compiles one file at a time, and each file is compiled in
total isolation from every other file. If `main.c` calls a function defined in `math_utils.c`, the compiler
building `main.c` has never seen `math_utils.c` and never will. Header files and the preprocessor exist
to solve exactly that problem - and nothing more. Once that clicks, the rest of this phase is just
mechanics.

## The preprocessor: a text editor that runs before the compiler

Before your compiler reads a single line of C grammar, a separate pass called the **preprocessor** runs
over your source file and rewrites it as plain text. It doesn't understand types, functions, or scope - it
understands lines starting with `#`, and it does simple text substitution. Only after the preprocessor is
done does the actual compiler see the result.

You can watch this happen. Take this file:

```c
#define PI 3.14159

int main(void) {
    double area = PI * 2 * 2;
    return 0;
}
```

Run just the preprocessor step (most compilers support this):

```console
$ gcc -E main.c
```

```c
int main(void) {
    double area = 3.14159 * 2 * 2;
    return 0;
}
```

`PI` is gone, replaced everywhere by `3.14159`, before the compiler even starts. That's the whole
preprocessor in one example: it's a text substitution pass, not a programming language feature. Keep that
model in your head for everything below.

## `#define`: macros

`#define NAME value` creates a macro - every later occurrence of `NAME` gets replaced with `value`, purely
textually.

```c
#define MAX_USERS 100
#define GREETING "Hello, friend"

int users[MAX_USERS];
```

Macros can also take arguments, acting like a function that's expanded inline:

```c
#define SQUARE(x) ((x) * (x))

int result = SQUARE(5);       // expands to ((5) * (5))
```

Notice the parentheses around `x` and around the whole expression. This isn't style - it's a real trap.
Without them:

```c
#define SQUARE(x) x * x

int result = SQUARE(2 + 3);   // expands to 2 + 3 * 2 + 3 = 11, not 25!
```

Because the preprocessor does dumb text substitution, `SQUARE(2 + 3)` literally becomes `2 + 3 * 2 + 3`,
and normal operator precedence takes over. Wrapping every parameter (and the full expression) in
parentheses is the standard defense: `((x) * (x))` expands to `((2 + 3) * (2 + 3))`, which is correct.

**When to reach for a macro vs. a real function.** Prefer a real function almost always - it type-checks
arguments, you can step through it in a debugger, and it doesn't have text-substitution surprises. Macros
still earn their keep for a few things a function can't do: defining constants used in array sizes (like
`MAX_USERS` above), conditional compilation (next section), and the rare case where you need code to work
across multiple types without templates (C has no generics). If a function would do the job, use a
function.

## Conditional compilation

`#ifdef`, `#ifndef`, `#if`, `#else`, and `#endif` let the preprocessor include or exclude chunks of code
before compilation even happens - useful for platform-specific code or debug-only logging:

```c
#define DEBUG

int main(void) {
#ifdef DEBUG
    printf("debug: starting up\n");
#endif
    printf("Hello, World!\n");
    return 0;
}
```

If `DEBUG` isn't defined, the preprocessor deletes the `printf("debug: ...")` line entirely - it's not
"skipped at runtime," it never reaches the compiler at all. This is exactly the mechanism header guards
use, which brings us to the actual point of this phase.

## Why header files exist

Back to the isolation problem. Say you split your code into two files:

```c
/* math_utils.c */
int add(int a, int b) {
    return a + b;
}
```

```c
/* main.c */
int main(void) {
    int result = add(2, 3);   // compiler has never seen add()!
    return 0;
}
```

When `gcc` compiles `main.c`, it hits `add(2, 3)` with no idea what `add` is - what it returns, what
arguments it takes, whether it even exists. It's not a linking problem yet; it's that the compiler needs a
**declaration** of `add` before it can generate correct code for the call.

A header file is nothing but a place to put those declarations, so any `.c` file can `#include` them:

```c
/* math_utils.h */
int add(int a, int b);   // declaration only - no body, ends in a semicolon
```

```c
/* math_utils.c */
#include "math_utils.h"

int add(int a, int b) {  // the actual definition
    return a + b;
}
```

```c
/* main.c */
#include "math_utils.h"

int main(void) {
    int result = add(2, 3);   // compiler now knows add's signature
    return 0;
}
```

Remember what `#include` actually does: it's the preprocessor, so `#include "math_utils.h"` is replaced,
textually, by the entire contents of `math_utils.h`, pasted right there. `main.c` after preprocessing
literally contains the line `int add(int a, int b);` before `main`. That's the entire mechanism - no
magic, no special compiler knowledge of "headers." It's copy-paste, done before compilation.

Now `main.c` compiles fine, because it has `add`'s signature. But `main.c` alone doesn't have `add`'s
*body* - that's in `math_utils.c`. The compiler produces an object file for `main.c` with a placeholder
that says "something named `add` goes here," and the **linker** (Phase 9 covers this) stitches the two
object files together, matching that placeholder to `add`'s real body from `math_utils.c`'s object file.
Build it like this:

```console
$ gcc -c math_utils.c -o math_utils.o
$ gcc -c main.c -o main.o
$ gcc math_utils.o main.o -o program
$ ./program
```

**The rule that falls out of this:** a header holds *declarations* (function signatures, struct
definitions, `#define` constants) - things a caller needs to know to use your code. The `.c` file holds
*definitions* - the actual function bodies. This split is what lets `main.c` and `math_utils.c` be compiled
completely separately, in any order, and still work together.

## `<angle brackets>` vs `"quotes"`

You've used both without thinking about the difference:

```c
#include <stdio.h>      // system/standard library headers
#include "math_utils.h" // your own project headers
```

`<...>` tells the preprocessor to search the compiler's standard system directories. `"..."` tells it to
look in your project's own directory first (then fall back to the system paths). Use quotes for headers
you wrote; angle brackets for the standard library and installed libraries.

## Structs and constants belong in headers too

Anything another file needs to know the *shape* of goes in the header, not just functions:

```c
/* point.h */
#ifndef POINT_H
#define POINT_H

typedef struct {
    int x;
    int y;
} Point;

Point point_add(Point a, Point b);

#endif
```

Every `.c` file that includes `point.h` now knows exactly how big a `Point` is and what fields it has,
which it needs at compile time - the compiler must know a struct's layout to generate code that touches
its fields, even before the linker does its job.

## Header guards: the problem of including the same header twice

Here's a real trap: if two headers both `#include "point.h"`, and a `.c` file includes both of *them*, the
preprocessor pastes `point.h`'s contents in twice. The compiler then sees `typedef struct { ... } Point;`
defined twice in the same file and rejects it - a redefinition error.

The fix is the `#ifndef` / `#define` / `#endif` pattern you saw above, called a **header guard**:

```c
#ifndef POINT_H
#define POINT_H

/* ... header contents ... */

#endif
```

Walk through what happens on the second inclusion. First time: `POINT_H` isn't defined yet, so
`#ifndef POINT_H` is true, the preprocessor defines `POINT_H` and includes the body. Second time (in the
same translation unit): `POINT_H` *is* now defined, so `#ifndef POINT_H` is false, and the preprocessor
skips straight to `#endif` - the body is never pasted in again. The name `POINT_H` is just a convention
(header name, uppercased, with `_` for `.`/`/`) - pick anything unique in your project, but match that
convention so nobody collides with it by accident.

Most compilers also support `#pragma once` as a shorter, non-standard alternative that does the same job
in one line at the top of the file. It's supported everywhere that matters in practice, but the classic
`#ifndef` guard is the one guaranteed by the C standard and the one you'll see in most real codebases, so
it's worth knowing both.

## Putting it together

A small multi-file project looks like this:

```
point.h        - declares the Point struct and point_add's signature
point.c        - #includes point.h, defines point_add's body
main.c         - #includes point.h, calls point_add
```

```c
/* point.h */
#ifndef POINT_H
#define POINT_H

typedef struct {
    int x;
    int y;
} Point;

Point point_add(Point a, Point b);

#endif
```

```c
/* point.c */
#include "point.h"

Point point_add(Point a, Point b) {
    Point result;
    result.x = a.x + b.x;
    result.y = a.y + b.y;
    return result;
}
```

```c
/* main.c */
#include <stdio.h>
#include "point.h"

int main(void) {
    Point p1 = {1, 2};
    Point p2 = {3, 4};
    Point sum = point_add(p1, p2);
    printf("(%d, %d)\n", sum.x, sum.y);
    return 0;
}
```

```console
$ gcc -c point.c -o point.o
$ gcc -c main.c -o main.o
$ gcc point.o main.o -o program
$ ./program
(4, 6)
```

Neither `.c` file ever saw the other's source code. `point.h` was the entire contract between them - and
the preprocessor is what made that contract visible to both, by pasting it in before compilation started.

## Recap

1. The **preprocessor** runs before the compiler and does text substitution - `#define`, `#include`,
   `#ifdef` are all preprocessor directives, not C language features.
2. `#include "file.h"` literally pastes that file's contents in, right there.
3. Headers hold **declarations** (what a caller needs to know); `.c` files hold **definitions** (the actual
   code) - that split is what lets files compile independently and get linked together after.
4. Wrap macro parameters and expressions in parentheses, or precedence will bite you.
5. **Header guards** (`#ifndef`/`#define`/`#endif`, or `#pragma once`) stop the same header from being
   pasted into a file twice and causing redefinition errors.

Phase 9 covers the tools that turn multiple `.c` files into one program automatically - Makefiles, and how
to actually debug the thing once it's built.

## Quick check

Test yourself on the ideas that make the rest of this guide's multi-file examples make sense:

```quiz
[
  {
    "q": "What does `#include \"math_utils.h\"` actually do?",
    "choices": [
      "The preprocessor pastes the entire text of math_utils.h into that spot, before compilation starts",
      "It tells the linker to look for math_utils.c when building the final program",
      "It imports the compiled math_utils.o object file into this source file",
      "It tells the compiler to search math_utils.c for any function it can't find"
    ],
    "answer": 0,
    "explain": "#include is a preprocessor directive, so it's a text substitution: the header's contents are copy-pasted in place before the compiler ever runs, not a special import or linking step."
  },
  {
    "q": "Why does `SQUARE(x)` need to be defined as `((x) * (x))` instead of `x * x`?",
    "choices": [
      "Because the preprocessor substitutes text literally, so SQUARE(2 + 3) without parentheses expands to 2 + 3 * 2 + 3 and normal operator precedence gives the wrong answer",
      "Because C requires all macro arguments to be wrapped in parentheses or it won't compile",
      "Because it makes the macro run faster at runtime",
      "Because otherwise SQUARE would be treated as a real function call"
    ],
    "answer": 0,
    "explain": "The preprocessor does dumb text substitution with no notion of precedence, so unparenthesized macro bodies and arguments can silently combine with surrounding operators in the wrong order."
  },
  {
    "q": "What problem do header guards (`#ifndef`/`#define`/`#endif`) actually solve?",
    "choices": [
      "They stop the same header's contents from being pasted into one file twice, which would otherwise redefine the same struct or type and fail to compile",
      "They prevent two different .c files in the project from both including the same header",
      "They make the linker skip duplicate function bodies across object files",
      "They speed up compilation by skipping headers that haven't changed"
    ],
    "answer": 0,
    "explain": "Without a guard, one file including two headers that both include a third header causes the preprocessor to paste that third header's contents in twice, and the compiler rejects the resulting duplicate struct/type definition."
  }
]
```
