Opening and closing files
Open, close, and check the status of files in C using fopen, fclose, and the FILE type — including mode strings and error handling.
- Open a file with fopen and check for failure
- Identify the common mode strings and their effects
- Close a file with fclose and understand why it matters
- Distinguish between text mode and binary mode
Every meaningful program eventually reads from or writes to persistent storage. C's file I/O is built around the FILE type — an opaque struct that represents an open file and manages the underlying buffering. The interface is part of the C standard library and works identically on Linux, macOS, and Windows (with some platform differences in text mode).
fopen — opening a file
FILE *fopen(const char *filename, const char *mode);fopen returns a FILE * on success, NULL on failure.
#include <stdio.h>
int main(void) {
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("fopen"); /* prints: "fopen: No such file or directory" */
return 1;
}
/* ... use the file ... */
fclose(fp);
return 0;
}perror(prefix) prints prefix: followed by the human-readable error message for the most recent system error. Always call it immediately after a failed system call.
Mode strings
| Mode | Effect |
|---|---|
"r" | Read. File must exist. |
"w" | Write. Creates file; truncates if exists. |
"a" | Append. Creates file; writes at end if exists. |
"r+" | Read and write. File must exist. |
"w+" | Read and write. Truncates or creates. |
"a+" | Read and append. |
"rb", "wb", etc. | Binary mode (no newline translation). |
On Unix/Linux, text mode and binary mode are identical — there is no newline translation. On Windows, text mode translates \n to \r\n on write and back on read. Use binary mode ("rb", "wb") when reading binary files to ensure no translation occurs on any platform.
fclose — closing a file
Always close files when you are done:
int fclose(FILE *fp);fclose flushes any buffered output to disk and releases the file descriptor. Returns 0 on success, EOF on error (the error is rare but possible if the final flush fails — e.g., disk full).
Failing to close a file has two consequences:
- Buffered output may not be written. The C library buffers writes for efficiency. If the program exits without flushing the buffer, the last writes are lost.
- File descriptor leak. Each open file uses a file descriptor (a small integer from the OS). Most systems limit each process to ~1,024 open files. Leaking descriptors eventually prevents opening new files.
Check fclose for write errors. If writing to a file, check fclose's return value. A full disk or network error may only manifest at close time when the OS flushes buffers. Silently ignoring fclose return values is a common bug in programs that write important data.
Checking if a file exists
Open with "r" and check for NULL:
FILE *fp = fopen("config.txt", "r");
if (fp == NULL) {
/* file does not exist or is not readable */
} else {
fclose(fp);
/* file exists */
}There is no standard C function to test file existence without opening. POSIX provides access() but that has race conditions. Opening is the correct way.
Standard streams
Three pre-opened FILE * values are always available:
| Name | Type | Description |
|---|---|---|
stdin | Input | Standard input — keyboard by default |
stdout | Output | Standard output — terminal by default |
stderr | Output | Standard error — terminal by default, unbuffered |
fprintf(stdout, "Normal output\n");
fprintf(stderr, "Error: something went wrong\n");stderr is typically unbuffered — writes appear immediately even if stdout is being redirected. Always write error messages to stderr, not stdout.
A pattern for robust file operations
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *fp = fopen("output.txt", "w");
if (!fp) {
perror("fopen");
return EXIT_FAILURE;
}
fprintf(fp, "Hello from C\n");
fprintf(fp, "Line 2\n");
if (fclose(fp) != 0) {
perror("fclose");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}EXIT_SUCCESS (0) and EXIT_FAILURE (1) from <stdlib.h> make the intent explicit.
Where to go next
Next: reading and writing text files — fscanf, fgets, fprintf, and fputs for working with line-oriented text data.
Lab: Linked list in C
Build a complete singly linked list in C — supporting push_front, push_back, delete, search, and clean deallocation.
Reading and writing text files
Read and write line-oriented text files in C using fgets, fputs, fprintf, and fscanf — with examples for line processing and formatted output.