Bytes on disk Ready
main.c

                

Ready

Statements run 0 Lines printed 0
Ready

Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode

How it works

The ideas behind the lab

Exactly what you watched happen, in plain steps.

Text files vs binary files

A text file holds characters: the number 1000000 becomes seven digits, readable in any editor, and is written with fprintf and read with fscanf. A binary file holds the raw bytes from memory: fwrite copies sizeof(n) bytes as they are, and fread copies them back. Binary files are compact, exact and fast — ideal for arrays and structs — and with fseek you can jump straight to any record.

int n = 1000000;

fprintf(text, "%d", n);            // 7 bytes: '1' '0' '0' …
fwrite(&n, sizeof n, 1, bin);       // 4 bytes: 40 42 0f 00

fread(&n, sizeof n, 1, bin);        // read it back
fseek(bin, 0, SEEK_SET);            // jump to the start
long where = ftell(bin);            // current position
Functions for binary files and positions
Call What it does Returns
fwrite(ptr, size, count, fp) writes count items of size bytes from ptr items written
fread(ptr, size, count, fp) reads up to count items into ptr items read (less at the end)
fseek(fp, offset, SEEK_SET) moves to offset from the start 0 on success
fseek(fp, offset, SEEK_CUR / SEEK_END) moves from the current position / the end 0 on success
ftell(fp) tells the current position a long
rewind(fp) back to position 0 —

Binary files

  1. Open with a b in the mode: "wb", "rb", "ab".
  2. fwrite(&x, sizeof x, 1, fp) saves one variable; fwrite(arr, sizeof arr[0], n, fp) a whole array.
  3. fread uses exactly the same arguments to load the data back.
  4. Check the return value: fewer items than asked means end of file or an error.
  5. Binary files depend on the machine (sizes, byte order) — text files are portable.

Moving around

  1. Every open file has a position; reads and writes move it on.
  2. fseek(fp, k, SEEK_SET) jumps to byte k; SEEK_CUR and SEEK_END count from elsewhere.
  3. Record i of a binary file of structs starts at i * sizeof(struct).
  4. ftell(fp) gives the position; after fseek(fp, 0, SEEK_END) it is the file size.
  5. rewind(fp) goes back to the start.

Errors and safe habits

  1. Check every fopen; report problems with perror("name").
  2. Return a non-zero exit code (return 1) when the program cannot do its job.
  3. Close every file on every path — also before an early return.
  4. Use "a" to add to logs; "w" only when the old content may go.
  5. ferror(fp) tells whether a read or write failed; feof(fp) whether the end was reached.
Walkthrough

Save an array of structs, then read one record directly

Every line, explained.

1 #include <stdio.h>
2
3 typedef struct {
4 int id;
5 double price;
6 } Item; 4 + 4 padding + 8 = 16 bytes.
7
8 int main(void)
9 {
10 Item items[3] = {{1, 9.5}, {2, 20.0}, {3, 4.25}};
11 FILE *fp = fopen("items.bin", "wb");
12 if (fp == NULL) { perror("items.bin"); return 1; }
13 size_t written = fwrite(items, sizeof(Item), 3, fp); The whole array in one call.
14 fclose(fp);
15 printf("%zu items written\n", written); Prints 3 items written
16
17 fp = fopen("items.bin", "rb");
18 if (fp == NULL) { perror("items.bin"); return 1; }
19 Item one;
20 fseek(fp, 2 * sizeof(Item), SEEK_SET); Jump straight to record 2 (the third).
21 if (fread(&one, sizeof(Item), 1, fp) == 1)
22 printf("id %d costs %.2f\n", one.id, one.price); Prints id 3 costs 4.25
23 fseek(fp, 0, SEEK_END);
24 printf("file size %ld bytes\n", ftell(fp)); Prints file size 48 bytes
25 fclose(fp);
26 return 0;
27 }
Common mistakes

Spot the bug

Mistakes almost every beginner makes — and how to fix them.

Reading a binary file as text

✗ Wrong
fwrite(&n, sizeof n, 1, fp);
…
fscanf(fp, "%d", &n);   // garbage
✓ Right
fwrite(&n, sizeof n, 1, fp);
…
fread(&n, sizeof n, 1, fp);

Read data the same way it was written: fprintf ↔ fscanf, fwrite ↔ fread.

Mixing up size and count

✗ Wrong
fwrite(arr, 10, sizeof(int), fp);   // works by luck
✓ Right
fwrite(arr, sizeof(int), 10, fp);   // size first, then count

The return value counts items of the given size; with the arguments swapped it means something else.

Ignoring the return value of fread

✗ Wrong
fread(&rec, sizeof rec, 1, fp);
print(rec);   // maybe old data
✓ Right
if (fread(&rec, sizeof rec, 1, fp) == 1)
    print(rec);

At the end of the file fread returns 0 and leaves rec unchanged.

Seeking by record number instead of bytes

✗ Wrong
fseek(fp, 2, SEEK_SET);   // byte 2, not record 2
✓ Right
fseek(fp, 2 * sizeof(Item), SEEK_SET);

fseek counts bytes. Multiply by the record size.

Leaving a file open on an error path

✗ Wrong
FILE *out = fopen("b.txt", "w");
if (in == NULL) return 1;   // out never closed
✓ Right
if (in == NULL) { fclose(out); return 1; }

Every opened file must be closed, also when the program gives up early. Better: open in an order that avoids it.

Practice

Check your understanding

Answer each question — you will see the explanation straight away.

0 / 5 correct
Question 1

How many bytes does fprintf(fp, "%d", 12345) write?

Question 2

How many bytes does fwrite(&n, sizeof n, 1, fp) write for an int n (4-byte int)?

Question 3

A file contains "ABCDEF". After fseek(fp, 3, SEEK_SET), what does fgetc return?

Question 4

How do you find the size of a file in bytes?

Question 5

Records of 16 bytes are stored in a binary file. Where does record 5 (counting from 0) start?

Assessment

More File Handling quiz

Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.