Programming lesson
C Structs to Binary Files: A Hands-On Guide for CS 1081 Assignment #12
Learn how to write and read C structs to both text and binary files with this step-by-step tutorial. Perfect for completing Assignment #12 in Computer Science 1081.
Introduction: Why File I/O Matters in 2026
In today's data-driven world, from AI models saving checkpoints to gaming apps storing player profiles, the ability to efficiently write and read structured data to files is a fundamental programming skill. As of May 2026, with the rise of edge computing and real-time data processing, understanding binary file I/O in C gives you a performance edge—binary files are faster and more compact than text files. This tutorial walks you through a typical assignment: storing student records in both text and binary formats, validating data integrity, and mastering struct serialization.
Whether you're building a school management system or a simple leaderboard for a trending mobile game, the concepts here are directly applicable. Let's dive into the code and logic behind Assignment #12.
Step 1: Defining the StudentInfo Struct
First, we need a blueprint for our student data. In C, we use a struct to group related fields. The assignment specifies a char array for name (100 chars), an integer age, a double GPA, and a char grade. No string datatype—just raw char arrays.
#include <stdio.h>
#include <string.h>
#define NAME_LEN 100
struct studentInfo {
char name[NAME_LEN];
int age;
double gpa;
char grade;
};This struct is the foundation. Notice we use #define for the name length—good practice for maintainability. Think of it like a character template in a role-playing game (RPG): each student has fixed stats (name, age, GPA, grade) that we can save and load.
Step 2: Initializing an Array of Students
We create an array of 4 students with sample data. In real-world apps, this data might come from a database or user input, but here we hardcode for simplicity.
struct studentInfo students[4] = {
{"Alice Johnson", 20, 3.75, 'A'},
{"Bob Smith", 22, 2.90, 'B'},
{"Carol Davis", 19, 3.50, 'A'},
{"David Lee", 21, 3.10, 'B'}
};Each student is like a row in a spreadsheet. We'll later write this data to a text file and then to a binary file.
Step 3: Writing Struct Data to a Text File
Text files are human-readable but slower to parse. The assignment requires a specific format: age + space + gpa + space + grade + space + name + newline. GPA must have 3 digits of precision.
void writeTextFile(struct studentInfo arr[], int size, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) { perror("File open failed"); return; }
for (int i = 0; i < size; i++) {
fprintf(fp, "%d %.3f %c %s\n", arr[i].age, arr[i].gpa, arr[i].grade, arr[i].name);
}
fclose(fp);
}
// Usage:
writeTextFile(students, 4, "studentsOutput.txt");The %.3f ensures exactly three decimal places for GPA. This is crucial for later comparison when reading back. Think of it as formatting a tweet—you want consistent spacing for easy parsing.
Step 4: Creating a Second Text File with 8 Students
Now we simulate reading external data. Create a file named studentsIn.txt with 8 student records in the same format. You can manually type this or generate it programmatically. Here's an example snippet:
23 3.45 A Emma Wilson
20 2.80 B James Brown
22 3.90 A Sophia Martinez
... (8 total)This file represents data from another source, like a batch import from a school database. We'll read it into a second array.
Step 5: Reading Text File into an Array and Displaying
Reading structured text requires careful parsing. We use fscanf with the same format but note that %s stops at whitespace, so names must be single tokens (no spaces). The assignment implies names are single words (e.g., "Alice"), but if they contain spaces, you'd need a different approach. For now, assume single-word names.
int readTextFile(struct studentInfo arr[], int maxSize, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) { perror("File open failed"); return 0; }
int count = 0;
while (count < maxSize && fscanf(fp, "%d %lf %c %99s", &arr[count].age, &arr[count].gpa, &arr[count].grade, arr[count].name) == 4) {
count++;
}
fclose(fp);
return count;
}
// Display nicely
void displayStudents(struct studentInfo arr[], int size) {
printf("%-20s %-5s %-8s %-6s\n", "Name", "Age", "GPA", "Grade");
printf("----------------------------------------\n");
for (int i = 0; i < size; i++) {
printf("%-20s %-5d %-8.3f %-6c\n", arr[i].name, arr[i].age, arr[i].gpa, arr[i].grade);
}
}The display function formats output as a table—similar to a leaderboard in a gaming app. This makes it easy for users (or graders) to verify data.
Step 6: Writing to a Binary File
Binary files store data in the same format as in memory, making them faster to read/write. The key is to write the entire struct at once using fwrite, not field by field.
void writeBinaryFile(struct studentInfo arr[], int size, const char *filename) {
FILE *fp = fopen(filename, "wb");
if (!fp) { perror("File open failed"); return; }
for (int i = 0; i < size; i++) {
fwrite(&arr[i], sizeof(struct studentInfo), 1, fp);
}
fclose(fp);
}
// Usage after reading studentsIn.txt into secondArray:
writeBinaryFile(secondArray, 8, "studentsOutput.bin");Notice fwrite takes the address of the struct, size of one struct, count 1, and file pointer. This is like taking a snapshot of the entire object—efficient and exact. In contrast, text writing is like describing the object in words.
Step 7: Reading Binary File and Validating
To verify the binary write, we read it back into a third array and compare field-by-field with the original second array.
int readBinaryFile(struct studentInfo arr[], int maxSize, const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) { perror("File open failed"); return 0; }
int count = 0;
while (count < maxSize && fread(&arr[count], sizeof(struct studentInfo), 1, fp) == 1) {
count++;
}
fclose(fp);
return count;
}
int validate(struct studentInfo arr1[], struct studentInfo arr2[], int size) {
for (int i = 0; i < size; i++) {
if (strcmp(arr1[i].name, arr2[i].name) != 0) return 0;
if (arr1[i].age != arr2[i].age) return 0;
if (arr1[i].gpa != arr2[i].gpa) return 0;
if (arr1[i].grade != arr2[i].grade) return 0;
}
return 1;
}
// In main:
struct studentInfo thirdArray[8];
int n = readBinaryFile(thirdArray, 8, "studentsOutput.bin");
if (n == 8 && validate(secondArray, thirdArray, 8)) {
printf("Save file was successfully validated\n");
} else {
printf("Validation failed\n");
}The validation loop checks every field. This is similar to a checksum in a download—ensuring data integrity after transfer. In 2026, with AI models transferring large binary files, such validation is critical.
Complete Program Structure
Here's how the pieces fit together in main():
int main() {
// Step 2: Initialize first array
struct studentInfo students[4] = { ... };
// Step 3: Write to text file
writeTextFile(students, 4, "studentsOutput.txt");
// Step 5: Read from studentsIn.txt into second array
struct studentInfo secondArray[8];
int count = readTextFile(secondArray, 8, "studentsIn.txt");
displayStudents(secondArray, count);
// Step 6: Write second array to binary
writeBinaryFile(secondArray, count, "studentsOutput.bin");
// Step 7: Read binary into third array and validate
struct studentInfo thirdArray[8];
int n = readBinaryFile(thirdArray, 8, "studentsOutput.bin");
if (n == count && validate(secondArray, thirdArray, count)) {
printf("Save file was successfully validated\n");
} else {
printf("Validation failed\n");
}
return 0;
}Common Pitfalls and Tips
- Binary portability: Binary files are not portable across different systems (endianness, struct padding). For assignment purposes, it's fine, but in real-world apps consider serialization libraries.
- Name with spaces: The assignment's text format uses space-separated fields, so names must be single words. If you need multi-word names, use a delimiter like comma or fixed-width.
- GPA precision: In text, use
%.3f. In binary, the double is stored exactly, so precision is not an issue. - File modes: Always use
"wb"and"rb"for binary,"w"and"r"for text. Mixing them can cause errors on some systems (e.g., Windows translates newlines in text mode).
Real-World Connections: Gaming and AI
Binary file I/O is used in game development to save player progress (e.g., a struct with health, inventory, position). In AI, model weights are often saved as binary files for fast loading. The validation step mirrors how a game checks save file integrity to prevent cheating or corruption. As of 2026, with the explosion of AI-generated content, understanding binary serialization helps you build efficient data pipelines.
Conclusion
You've now completed a full cycle: writing structs to text and binary files, reading them back, and validating data. This assignment teaches fundamental file I/O skills that are timeless. Practice by extending the code—add error handling, support for multi-word names, or even a simple search function. Master these concepts, and you'll be ready for more advanced topics like databases or network serialization.
Happy coding, and may your files always be valid!