C Mastery / stdio.h: Streams, Files, Formatted I/O
Part 3 — The Standard Library

stdio.h: Streams, Files, Formatted I/O

This chapter covers <stdio.h>: streams, FILE *, the fopen/fclose/ fread/fwrite file functions, and the printf/scanf families. This is the standard I/O layer of ISO C.

Why This Matters

Every program that reads or writes data uses <stdio.h>. The buffered-stream model, formatted I/O specifiers, and their security implications are core practical knowledge.

Prerequisites

Core Concept

Streams and FILE

<stdio.h> models I/O as streams represented by FILE *. A stream is a buffered sequence of bytes. Three streams are opened automatically:

StreamPurpose
stdinstandard input
stdoutstandard output
stderrstandard error (unbuffered or line-buffered)

File functions

FILE *fopen(const char *path, const char *mode);
int   fclose(FILE *stream);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int   fseek(FILE *stream, long offset, int whence);
long  ftell(FILE *stream);
int   fflush(FILE *stream);

Modes include "r", "w", "a", "r+", "w+", "a+", with "b" for binary on some platforms.

Formatted output

int printf(const char *fmt, ...);
int fprintf(FILE *stream, const char *fmt, ...);
int snprintf(char *buf, size_t n, const char *fmt, ...);

snprintf is the safe choice: it writes at most n-1 characters plus a null terminator.

Formatted input

int scanf(const char *fmt, ...);
int fscanf(FILE *stream, const char *fmt, ...);
int sscanf(const char *s, const char *fmt, ...);

Syntax

Format specifiers (selected)

SpecifierType
%dint
%uunsigned int
%x/%Xunsigned int in hex
%cchar (int after promotion)
%schar * (null-terminated)
%fdouble
%pvoid *
%zusize_t
%lldlong long
%Lflong double

The printf/scanf format specifiers differ in some details (e.g., %f takes double for printf but float * for scanf).

Examples

Reading and writing a file

#include <stdio.h>

int main(void)
{
    FILE *f = fopen("data.txt", "w");
    if (f == NULL) return 1;
    fprintf(f, "value: %d\n", 42);
    fclose(f);

    f = fopen("data.txt", "r");
    if (f == NULL) return 1;
    int v = 0;
    fscanf(f, "value: %d", &v);
    printf("read %d\n", v);
    fclose(f);
    return 0;
}

Expected output: read 42 (and data.txt contains value: 42).

Safe formatted output with snprintf

#include <stdio.h>

int main(void)
{
    char buf[16];
    int n = snprintf(buf, sizeof buf, "%d", 12345);
    printf("wrote %d chars: %s\n", n, buf);
    return 0;
}

Binary read/write

#include <stdio.h>

int main(void)
{
    int data[4] = {1, 2, 3, 4};
    FILE *f = fopen("data.bin", "wb");
    fwrite(data, sizeof data[0], 4, f);
    fclose(f);

    int out[4] = {0};
    f = fopen("data.bin", "rb");
    fread(out, sizeof out[0], 4, f);
    fclose(f);
    return 0;
}

How It Works

stdio buffers data in memory to reduce the number of system calls. printf formats into the buffer; the buffer is flushed to the OS when full, when fflush is called, or at program exit. fread/fwrite move bytes between the buffer and your memory.

Variations

Buffering modes

setvbuf controls buffering (full, line, or unbuffered). stderr is typically unbuffered. This matters for interactive programs and logging.

Wide-character I/O

fwprintf, fwscanf, and friends operate on wide strings (c.stdlib.10).

Common Mistakes

correct for printf but scanf needs %f with float *).

Undefined Behavior

Portability

platforms (Windows); on POSIX, text and binary are identical.

Under the Hood

FILE is an opaque struct holding a buffer, position, and flags. printf formats into the buffer; when flushed, it writes via the OS. The compiler may optimize printf("literal") into fputs or puts.

Practical Usage

Exercises

1. Write a program that reads lines with fgets and prints them with line numbers. 2. Use snprintf to build a formatted string safely and verify truncation. 3. Demonstrate the %f/%lf difference between printf and scanf. 4. Write a binary file and read it back, checking fread's return value.

Deep Challenge

Implement a snprintf-style formatter for %d, %s, and %c that always null-terminates and never overflows its buffer. Explain how you handle truncation and the return value semantics (number of chars that *would* have been written).

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.lib.stdio06