# File Handling and the Preprocessor in C — fopen, fprintf, Macros, Header Guards

Source: https://skills101.in/learn/c-programming/c-files-and-preprocessor — Skills101 free tutorial (C Programming). Updated 2026-11-09.

Programs need to **save data** between runs — that's file handling. And before your code is even compiled, the **preprocessor** rewrites it — that's where macros and header files come in.

## Part A — Files

![Memory layout: file data you read is copied into buffers in your program's memory — the stack for small arrays or the heap for big ones](https://skills101.in/learn-img/c-programming/memory-layout.svg)

### Open modes

| Mode | Meaning |
|---|---|
| `"r"` | read; file must exist |
| `"w"` | write; creates or **erases** the file |
| `"a"` | append to the end |
| `"r+"`, `"w+"`, `"a+"` | read and write variants |
| add `b` (`"rb"`) | binary mode |

### Write, then read

```c
#include <stdio.h>

int main(void) {
    FILE *f = fopen("marks.txt", "w");
    if (f == NULL) { perror("open"); return 1; }
    fprintf(f, "Asha 92\nRavi 74\nNeha 85\n");
    fclose(f);

    f = fopen("marks.txt", "r");
    if (f == NULL) { perror("open"); return 1; }
    char name[20];
    int m, total = 0, count = 0;
    while (fscanf(f, "%19s %d", name, &m) == 2) {
        total += m;
        count++;
    }
    fclose(f);
    printf("%d students, average %.2f\n", count, (float)total / count);
    return 0;
}
```

```text
3 students, average 83.67
```

- `fopen` returns **NULL** if it fails (missing file, no permission) — always check.
- `fprintf` / `fscanf` work like printf / scanf but on a file.
- `fgets(buf, size, f)` reads a whole line safely; `fputs` writes a string.
- Always `fclose` — it flushes data to disk and frees the file handle.
- `fscanf` returns how many items it read; loop **while it returns the expected count** (better than `while (!feof(f))`).

### Command-line arguments

```c
int main(int argc, char *argv[]) {
    /* program.exe data.txt 5   →  argc = 3, argv[1] = "data.txt", argv[2] = "5" */
}
```

## Part B — The preprocessor

Lines starting with `#` are handled **before compilation** — pure text substitution.

### #define constants and macros

```c
#include <stdio.h>

#define PI 3.14159
#define SQUARE(x) ((x) * (x))
#define BAD_SQUARE(x) x * x
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main(void) {
    printf("%.2f\n", PI * SQUARE(2));
    printf("%d %d\n", SQUARE(2 + 3), BAD_SQUARE(2 + 3));
    printf("%d\n", MAX(7, 12));
    return 0;
}
```

```text
12.57
25 11
12
```

`BAD_SQUARE(2 + 3)` becomes `2 + 3 * 2 + 3` = **11**, not 25! Always put brackets around every parameter **and** the whole macro. (This exact question appears in many placement tests.)

**Macro vs function:** macros are copied in place (no call overhead) but have no type checking and can evaluate arguments twice (`SQUARE(i++)` is a bug). Modern C prefers `static inline` functions.

### #include

- `#include <stdio.h>` — search system folders.
- `#include "myfile.h"` — search your project folder first.

### Header guards

Stop a header from being included twice:

```c
#ifndef STUDENT_H
#define STUDENT_H

typedef struct { int roll; char name[20]; } Student;
void print_student(const Student *s);

#endif
```

### Conditional compilation

```c
#ifdef DEBUG
    printf("x = %d\n", x);     /* only compiled when DEBUG is defined */
#endif
```

Compile with `gcc -DDEBUG main.c` to turn it on.

## Common mistakes

- Not checking if `fopen` returned NULL.
- Opening with `"w"` and wiping a file you meant to add to (use `"a"`).
- Macros without brackets; macros with side effects like `SQUARE(i++)`.

## Quick recap

- fopen (check NULL) → fprintf/fscanf/fgets → fclose. Modes r, w (erases), a.
- Loop on fscanf's return value.
- Preprocessor = text substitution before compiling.
- Bracket every macro parameter; use header guards in .h files.


Practice quiz and free certificate: https://skills101.in/learn/c-programming/c-files-and-preprocessor