C
File I/O & Standard Library
Every C program so far has printed to the screen and read from the keyboard — both are, underneath,
just special cases of a more general idea: a stream you can read from or write to. This
chapter covers reading and writing actual files on disk, plus a tour of the wider standard library beyond
stdio.h worth knowing by name.
Quick reference
- What it is
- The
FILE*-based API (fopen,fread,fwrite,fclose) for reading and writing files, plus the rest of C's standard library. - Mental model
- A file, once opened, behaves like a one-directional (or two-directional) hose — you read bytes out of it or write bytes into it, in order, tracked by an internal position that advances as you go.
- Key numbers
- Every successful
fopenmust be matched by exactly onefclose— a leaked, never-closed file handle is a real, accumulating resource leak. - Reach for it when
- Persisting data across program runs (logs, config, saved state) — anything that needs to survive after the program exits.
Opening, using, and closing a file
#include <stdio.h>
int main(void) {
FILE *f = fopen("log.txt", "w"); // open for writing
if (f == NULL) {
printf("Failed to open file\n");
return 1;
}
fprintf(f, "Reading: %d\n", 42); // works just like printf, but to a file
fclose(f); // ALWAYS close what you open
return 0;
}
fclose — including the error path, if the file was actually opened before something else failed.File modes
| Mode | Meaning |
|---|---|
"r" | Read — fails (NULL) if the file doesn't exist. |
"w" | Write — creates the file if missing, erases existing contents if present. |
"a" | Append — creates the file if missing, writes are added to the end without erasing. |
"rb", "wb" | Same as above, but binary mode — no text-mode newline translation. |
Reading and writing
// Writing raw bytes
FILE *f = fopen("data.bin", "wb");
int values[3] = {10, 20, 30};
fwrite(values, sizeof(int), 3, f); // write 3 ints
fclose(f);
// Reading them back
FILE *in = fopen("data.bin", "rb");
int loaded[3];
fread(loaded, sizeof(int), 3, in); // read 3 ints
fclose(in);
// Line-by-line text reading
FILE *log = fopen("log.txt", "r");
char line[256];
while (fgets(line, sizeof(line), log) != NULL) {
printf("%s", line);
}
fclose(log);
fread/fwrite move raw bytes and are the natural fit for binary data;
fgets/fprintf work at the level of text and lines, the natural fit for
human-readable files.
A tour of the wider standard library
Introduction to C listed these headers by name; here's
what actually lives in the ones beyond stdio.h and string.h:
stdlib.hatoi/atof(string-to-number),rand/srand(pseudo-random numbers),exit(terminate immediately),malloc/free(covered fully in Embedded C's Dynamic Memory).math.hsqrt,pow,fabs,sin/cos/tan,floor/ceil— floating-point math the language itself has no built-in operators for.ctype.hisdigit,isalpha,isspace(character classification),toupper/tolower(case conversion) — one character at a time.time.htime(current time),clock(CPU time elapsed) — the basis for timestamps and simple performance measurement.
Real-world applications
- Configuration files — reading a settings file line by line at program startup with
fgets. - Data logging — appending sensor readings to a log file with
"a"mode, common in any long-running data-collection program. - Saving/loading program state — writing a struct's raw bytes with
fwriteand reading them back later withfreadis a simple (if fragile — see Gotchas) way to persist data.
Gotchas
Field notes
- Never assume
fopensucceeded — a missing file, a permissions problem, or a full disk all make it returnNULL, and dereferencing aNULLFILE*afterward is undefined behavior. Always check before using it. - Opening in
"w"mode silently erases the file's existing contents the instant it's opened, even if you never actually write anything — a common source of accidental data loss. - Writing a struct's raw bytes with
fwrite(as in the applications above) bakes in your specific compiler's padding/alignment and byte order — a file written this way isn't guaranteed portable to a different compiler, platform, or even a future version of the same program. - Forgetting
fcloseleaks the file handle for the life of the program — usually harmless in a short-lived script, but a genuine, accumulating resource problem in any long-running program (including nearly all embedded firmware).