fopen(path, mode) opens a file and returns a FILE pointer (or NULL on failure) — mode strings like "r", "w", "a" control read/write/append behavior and whether the file is treated as text or binary. Line-oriented functions like fgets/fputs handle text a line at a time; fread/fwrite handle raw binary data in fixed-size blocks. Every successfully opened file must eventually be closed with fclose, which flushes any buffered output to disk — skipping it risks losing buffered writes that were never actually flushed, not just a resource leak.
Reading a text file line by line
fgets reads up to one line (or buffer-size bytes, whichever comes first) and returns NULL once it hits end-of-file — checking that return value is what correctly ends the loop.
FILE *f = fopen("data.txt", "r");
if (f == NULL) {
perror("fopen failed");
return 1;
}
char line[256];
while (fgets(line, sizeof(line), f) != NULL) {
printf("%s", line);
}
fclose(f); // required — flushes and releases the file handleWriting to a file
"w" mode truncates an existing file to zero length before writing (destroying its previous contents); "a" mode appends to the end instead — confusing the two is a real, easy way to accidentally lose data.
FILE *f = fopen("output.txt", "w"); // "w" TRUNCATES any existing content
if (f == NULL) {
perror("fopen failed");
return 1;
}
fprintf(f, "Line %d\n", 1);
fputs("Another line\n", f);
fclose(f);Binary I/O: fread and fwrite
fread's return value is the number of complete elements actually read, which can be less than requested at end-of-file or on error — always check it rather than assuming the full request succeeded.
int numbers[5] = {1, 2, 3, 4, 5};
FILE *f = fopen("data.bin", "wb"); // "b" for binary mode
fwrite(numbers, sizeof(int), 5, f);
fclose(f);
int loaded[5];
f = fopen("data.bin", "rb");
size_t count = fread(loaded, sizeof(int), 5, f); // check count against the expected 5
fclose(f);