File Handling · intermediate · ~10 min

fopen and the stdio model

Open files for reading/writing/appending.

Overview

fopen(path, mode) opens a file and returns a FILE * you use with fread, fwrite, fprintf, fgets, etc. The mode is "r", "w", "a", with optional + (read+write) and b (binary on Windows; no-op on Unix). Every successful fopen needs a matching fclose.

Why it matters

Files are how programs persist data and exchange it with other programs and humans. Reading configuration, writing logs, processing CSV, streaming a download to disk — all build on fopen + a tiny family of stdio functions. Mastering them is the difference between toy programs and real tools.

Core concepts

Buffered I/O. stdio buffers your reads and writes in a small (~4 KB) memory buffer — flushed automatically on fclose, on \n for line-buffered streams, or explicitly via fflush. Modes. r (read, file must exist), w (write, truncate), a (append, create if needed). Add + to allow both read and write. Error checking. fopen returns NULL on failure; check errno for the reason (perror("fopen") prints a friendly message).

Syntax notes

FILE *fp = fopen("data.txt", "r");
if (!fp) { perror("fopen"); return 1; }
char line[256];
while (fgets(line, sizeof line, fp)) {
    /* process line — note it includes the trailing \n if it fit */
}
fclose(fp);

Lesson

FILE *fopen(const char *path, const char *mode) returns a stream pointer or NULL. Common modes:

  • "r" read text, error if missing.
  • "w" write text, truncates or creates.
  • "a" append text, creates if missing.
  • Add "b" for binary ("rb", "wb").

Always pair with fclose. Check both fopen and fclose return values.

Code examples

FILE *f = fopen("log.txt", "a");
if (!f) { perror("fopen"); return 1; }
fprintf(f, "%s\n", msg);
if (fclose(f) != 0) perror("fclose");

Common mistakes

  • Ignoring fclose — a successful write isn't actually flushed to disk until close.
  • Forgetting "b" on Windows: text-mode translates \n\r\n.

Debugging tips

When fopen returns NULL, perror (or strerror(errno)) tells you why — usually 'No such file' or 'Permission denied'. If you're getting unexpected content, check the mode: "r" on a directory still 'succeeds' on some Unixes, then fread fails weirdly. Use absolute paths until your relative paths are correct.

Memory safety

Every fopen needs a fclose, even on error paths — goto cleanup patterns are common in C exactly for this reason. Forgetting fclose leaks file descriptors; on a busy server, you'll run out of them and fopen will start returning 'Too many open files'.

Real-world uses

Reading config files, parsing CSV, processing log files line by line, writing reports, saving game state, importing/exporting data.

Practice tasks

  1. Read a file and print its size in bytes. 2. Count the lines in a file using fgets. 3. Copy a file byte-by-byte using fread/fwrite. 4. Open the same file twice and compare positions with ftell.

Summary

fopen opens, fclose closes; in between you use fread / fgets / fprintf / fwrite. Check the return, choose the right mode, and never forget to close — and you have a robust file pipeline for any text or binary data.

Practice with these exercises