File I/O in C
File I/O connects your program to persistent storage. The stdio library provides buffered streams; POSIX adds lower-level file descriptors.
Opening and Closing
#include <stdio.h>
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
/* ... use fp ... */
fclose(fp);
Text I/O
fprintf(fp, "Count: %d\n", count);
fscanf(fp, "%d", &count);
char line[256];
while (fgets(line, sizeof line, fp)) {
printf("%s", line);
}
Binary I/O
typedef struct { int id; double value; } Record;
Record rec = { .id = 1, .value = 3.14 };
FILE *fp = fopen("data.bin", "wb");
fwrite(&rec, sizeof rec, 1, fp);
fclose(fp);
fp = fopen("data.bin", "rb");
Record read_back;
fread(&read_back, sizeof read_back, 1, fp);
fclose(fp);
Binary files are not portable across endianness differences.
File Modes
| Mode | Meaning |
|---|---|
"r" |
Read |
"w" |
Write (truncate) |
"a" |
Append |
"r+" |
Read/write |
"rb" |
Binary read |
Error Handling
if (ferror(fp)) { perror("read error"); }
if (feof(fp)) { /* end of file reached */ }
/* Always check return values */
if (fclose(fp) != 0) perror("fclose");
Common Pitfalls
- Treating compiler warnings as optional rather than actionable feedback.
- Skipping error checks on library and system calls.
- Copy-pasting examples without adapting to your project’s conventions.
Best Practices
- Enable strict compiler warnings and fix them before merging.
- Write small, testable units with clear input/output contracts.
- Document non-obvious invariants and preconditions.
- Use version control and code review for every change.
Memory and Performance Notes
stdio uses a buffer (typically 4–8 KB). Use fflush before interleaving stdout/stderr or setvbuf for custom buffering.
Exercise
Write a program that copies a text file line-by-line, numbering each line in the output.
Hint: Use snprintf to prepend line numbers. Handle files that don’t end with a newline.
Summary
Apply these concepts in small programs before moving to larger projects. Combine with adjacent topics in the learning path for deeper mastery.
Real-World Application
These concepts appear in production codebases — from operating system kernels to embedded firmware. Study open-source projects that use this topic extensively to see idiomatic patterns at scale.
Debugging Checklist
- Reproduce the issue with the smallest possible input.
- Enable compiler warnings and sanitizers.
- Use a debugger to inspect state at the failure point.
- Verify assumptions about types, sizes, and return values.
- Compare working and broken code paths side by side.
- Write a regression test once the bug is fixed.
Further Reading
Consult the ISO C standard, Effective C by Robert C. Seacord, and your compiler documentation for platform-specific behavior.
Quick Reference
Review the code examples on this page before starting the exercise. Type them manually to build muscle memory.
Additional Examples
Consider how this topic applies in a larger project:
// Break the problem into smaller functions
// Test each function independently
// Integrate incrementally
Working through variations of the examples above builds deeper understanding than reading alone.
Interview and Review Questions
- Explain the core concept of this topic in your own words.
- What happens when this code runs with edge-case input (empty, null, zero, max value)?
- How would you debug a bug related to this topic in production?
- What are the performance implications of the approach shown here?
- How does this feature compare to the equivalent in another language you know?