The Terminal Left Behind

Four lessons ago, everything your programs computed died at exit. Now they write, read back, index a file like an array, and take orders from the shell. The chapter added remarkably little machinery, one type (FILE *), an f prefix on the I/O you already knew, three positioning functions, and a second form of main, because everything else was chapters 4 through 11 aimed at streams. The recap, then one program that uses all of it.

Opening and Closing

fopen(path, mode) returns a stream handle or NULL, and NULL genuinely arrives: missing files, bad paths, no permission, full disks. The mode string sets the relationship: "r" reads an existing file (NULL if absent), "w" writes fresh and truncates anything already there, the data-loss trap, and "a" appends, creating if needed and preserving what exists; a b suffix selects binary mode. Every successful fopen gets exactly one fclose on every path out of the code, buffered output is not safely on disk until then, and a closed FILE * is the file world's dangling pointer. The reveal underneath: stdin, stdout, and stderr are pre-opened FILE *s, so printf(...) was fprintf(stdout, ...) all along.

Text Streams

The f-family is your console I/O with the stream made explicit, format language intact, and a text format is a contract between the writing code and the reading code. while (fscanf(in, "%d", &v) == 1) is chapter 6's sentinel loop consuming a file; the count alone cannot tell clean end-of-file from a malformed token, and feof(in) answers afterwards when the difference matters. fgets until NULL processes a file line by line, and fgetc/getc until EOF, banked in an int for EOF's sake, powers the canonical copy program, whose failure branch closes whichever stream did open.

Binary and Random Access

fwrite(ptr, size, count, stream) copies memory to disk byte for byte; fread mirrors it back. Both return the count of complete items transferred, and both checks are load-bearing: a short fwrite means a failed write, and a short fread leaves the unfilled items as uninitialized memory. Sizes are always sizeof-driven, padding included, never summed from the members. Fixed-size records turn the file into an array: record i starts at byte i * sizeof(struct T), fseek(f, offset, whence) jumps there against a SEEK_SET, SEEK_CUR, or SEEK_END anchor, and seek-to-end plus ftell is the file-size idiom. The bill for binary's speed and exact round-trips: not human-readable, and not portable, because padding and endianness make the file's layout the writing machine's business.

Command-Line Arguments

int main(int argc, char *argv[]) is main's second standard form: argc counts the arguments, argv holds pointers to NUL-terminated strings, argv[0] is the program's own name and is counted, and argv[argc] is NULL by guarantee. Check argc before touching argv[i], indexing past the count is undefined behaviour, and fail politely with a usage line naming argv[0]. Arguments arrive as text: atoi returns 0 on failure with no error signal, indistinguishable from a real zero, while strtol with the end-pointer guard (end == text || *end != '\0') converts honestly. Arguments beat prompts because shells, makefiles, and other programs can supply them; only a human can answer a prompt.

One Program, Whole Chapter

Four struct records to disk in binary, the size counted by seek-and-tell without reading a record, one record fetched by offset arithmetic, a text pass summed by the sentinel loop, and argv[0] naming the program, every fopen checked, every stream closed on every path. Here it prints:

/tmp/out started with argc 1
32 bytes on disk, 4 records
record 2: id 3, score 91
4 scores, total 286

Trace each line back to its lesson, then take the quiz.

Looking Ahead

Chapter 13 is the course's summit: dynamic memory. malloc, calloc, realloc, and free let a program size its storage at run time instead of at compile time, ownership becomes a discipline you enforce the way you enforced fclose, and it all culminates where every C syllabus does, the linked list: structs pointing at structs, grown one node at a time.