Step 3 of 8
When fopen fails
Files are the first thing in this course that can fail for reasons outside your program: the file doesn't exist, you don't have permission, the disk is full, another program has it locked. Code that works with files must expect failure.
fopen returns NULL when it can't open the file, and on common systems it also sets the global errno to a code describing why. perror(message) prints your message plus a readable description of errno to stderr (the error output stream):
FILE *f = fopen("config.txt", "r");
if (f == NULL) {
perror("config.txt"); /* config.txt: No such file or directory */
return -1;
}
Return an error code
A function that works with files usually reports failure through its return value, with a value that can't be a real result: -1 for a count, NULL for a pointer. The caller checks it and decides what to do.
#include <stdio.h>
long file_size(const char *path) {
FILE *f = fopen(path, "r");
if (f == NULL) {
return -1;
}
long n = 0;
while (fgetc(f) != EOF) {
n++;
}
fclose(f);
return n;
}
int main(void) {
FILE *f = fopen("note.txt", "w");
if (!f) return 1;
fputs("hello\n", f);
fclose(f);
printf("%ld %ld\n", file_size("note.txt"), file_size("missing.txt"));
return 0;
}
6 -1
Close on every path
Every successful fopen needs exactly one fclose, including on early returns after later errors. Operating systems limit how many files a program can have open, so leaked handles eventually make fopen fail. A tidy structure is: open, check, do the work, close, then return.
fgetc returns an int, not a char, because it must be able to return every possible byte value plus the special EOF value. Store it in an int before comparing with EOF.
Your turn: write long count_lines(const char *path) that returns the number of '\n' characters in the file, or -1 if it can't be opened. Read with fgetc, which returns one character at a time, or EOF at the end.