C Programming · Lesson 9 of 10 · Updated ·
File Handling and the Preprocessor in C — fopen, fprintf, Macros, Header Guards
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
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
#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;
}
3 students, average 83.67
fopenreturns NULL if it fails (missing file, no permission) — always check.fprintf/fscanfwork like printf / scanf but on a file.fgets(buf, size, f)reads a whole line safely;fputswrites a string.- Always
fclose— it flushes data to disk and frees the file handle. fscanfreturns how many items it read; loop while it returns the expected count (better thanwhile (!feof(f))).
Command-line arguments
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
#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;
}
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:
#ifndef STUDENT_H
#define STUDENT_H
typedef struct { int roll; char name[20]; } Student;
void print_student(const Student *s);
#endif
Conditional compilation
#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
fopenreturned 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.