LearnFrenzy
Sign in Take a free test

File Handling · Lesson 32 of 35

Binary Files and Random Access in C

5 min read Updated Nov 7, 2026 Saurabh Samir

Text files are easy to read but slow to search: to find record 500 you must read the 499 before it. A binary file stores data exactly as it sits in memory. With fixed-size records you can jump straight to any record with fseek – the idea behind databases and the classic "student record management" mini project.

In this lesson

  • Writing and reading structures with fwrite and fread
  • Jumping to a record with fseek and finding size with ftell
  • Updating a record in place
  • Text vs binary files

Writing and reading records

#include <stdio.h>

typedef struct {
    int roll;
    char name[20];
    float marks;
} Student;

int main(void) {
    Student cls[] = {{1, "Asha", 88.5f}, {2, "Ravi", 74.0f}, {3, "Meera", 91.0f}, {4, "Kiran", 66.5f}};
    int n = sizeof cls / sizeof cls[0];

    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) { perror("students.dat"); return 1; }
    size_t written = fwrite(cls, sizeof cls[0], (size_t)n, fp);   // the whole array in one call
    fclose(fp);
    printf("wrote %zu records of %zu bytes\n", written, sizeof(Student));

    fp = fopen("students.dat", "rb");
    if (fp == NULL) { perror("students.dat"); return 1; }
    Student s;
    while (fread(&s, sizeof s, 1, fp) == 1) {                    // one record at a time
        printf("%d %-6s %.1f\n", s.roll, s.name, s.marks);
    }
    fclose(fp);
    return 0;
}

Output

wrote 4 records of 28 bytes
1 Asha   88.5
2 Ravi   74.0
3 Meera  91.0
4 Kiran  66.5

fwrite(ptr, size, count, fp) writes count items of size bytes; fread reads the same way and returns how many complete items it read – loop while it returns what you asked for.

Random access with fseek and ftell

#include <stdio.h>

typedef struct { int roll; char name[20]; float marks; } Student;

int main(void) {
    Student cls[] = {{1, "Asha", 88.5f}, {2, "Ravi", 74.0f}, {3, "Meera", 91.0f}, {4, "Kiran", 66.5f}};
    FILE *fp = fopen("students.dat", "wb");
    if (fp == NULL) return 1;
    fwrite(cls, sizeof cls[0], 4, fp);
    fclose(fp);

    fp = fopen("students.dat", "r+b");                    // read and update
    if (fp == NULL) return 1;

    fseek(fp, 0, SEEK_END);                               // jump to the end
    long bytes = ftell(fp);                               // position = file size
    printf("file size %ld bytes = %ld records\n", bytes, bytes / (long)sizeof(Student));

    Student s;
    fseek(fp, 2 * (long)sizeof(Student), SEEK_SET);       // record #3 (index 2), no reading of 1 and 2
    fread(&s, sizeof s, 1, fp);
    printf("record 3: %s %.1f\n", s.name, s.marks);

    s.marks = 95.0f;                                      // update it in place
    fseek(fp, -(long)sizeof(Student), SEEK_CUR);          // step back one record
    fwrite(&s, sizeof s, 1, fp);

    rewind(fp);
    while (fread(&s, sizeof s, 1, fp) == 1) printf("%d %-6s %.1f\n", s.roll, s.name, s.marks);
    fclose(fp);
    return 0;
}

Output

file size 112 bytes = 4 records
record 3: Meera 91.0
1 Asha   88.5
2 Ravi   74.0
3 Meera  95.0
4 Kiran  66.5

Finding record k (0-based)

  1. Offsetk × sizeof(Student) bytes from the start
  2. fseek(fp, offset, SEEK_SET)jump there directly
  3. fread one recordno need to read the earlier ones
FunctionDoes
fseek(fp, offset, SEEK_SET)move to offset bytes from the start
fseek(fp, offset, SEEK_CUR)move relative to the current position
fseek(fp, offset, SEEK_END)move relative to the end
ftell(fp)current position in bytes
rewind(fp)back to the start

Switching between reading and writing

In "r+" mode you must call fseek (or fflush) between a read and a following write – as the example does with the SEEK_CUR step back. Skipping it is undefined behaviour.

Text vs binary files

Text fileBinary file
Contentshuman-readable charactersraw bytes, exactly as in memory
Size of 88.54 characters ("88.5")4 bytes (a float), always
Random accesshard – lines have different lengthseasy with fixed-size records
Portable between systemsyesno – depends on sizes, padding and byte order
Good forconfigs, logs, CSV exportsrecords, images, fast save/load

Portability

A binary file written on one machine may not read correctly on another with different int sizes, struct padding or byte order (endianness). For data shared between systems, use text formats (CSV, JSON) or write each field with a fixed size and byte order.

Practice

Try it yourself

  1. Write the numbers 1–10 as ints to a binary file, then read back and print only the 5th number using fseek.

    Show solution
    #include <stdio.h>
    
    int main(void) {
        FILE *fp = fopen("nums.bin", "w+b");
        if (fp == NULL) return 1;
        for (int i = 1; i <= 10; i++) fwrite(&i, sizeof i, 1, fp);
        int x;
        fseek(fp, 4 * (long)sizeof x, SEEK_SET);
        fread(&x, sizeof x, 1, fp);
        printf("5th number: %d\n", x);
        fclose(fp);
        return 0;
    }

    Output

    5th number: 5

Quick check

What does fread return?

The number of complete items read.

How do you find a file's size in bytes?

fseek(fp, 0, SEEK_END); long size = ftell(fp);

Why are binary files fast for record lookups?

Fixed-size records let you compute any record's offset and fseek straight to it.

Summary

  • fwrite/fread move raw bytes – whole structs or arrays at once (open with "wb"/"rb").
  • fseek + ftell give random access; update records in place with "r+b".
  • Binary is compact and fast but not portable; use text formats to share data.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links