Module 5 : Data Types (Struct, Enum, TypeDef) & File I/O

By the end of this module, students will be able to:

- Understand and implement user-defined data types using `struct`

- Utilize `enum` for creating readable constant sets

- Apply `typedef` to create type aliases for better code readability

- Perform file input/output operations in C

- Handle text and binary files effectively

- Design programs that persist data using files

- Combine structs with file I/O for data management systems

1. Introduction to User-Defined Data Types

1.1 Why User-Defined Types?

In previous modules, we learned about basic data types like int, float, char, etc. These are sufficient for simple programs, but real-world applications often require more complex data structures.

Example Problem: Suppose you want to store information about a student:

// Without user-defined types - difficult to manage
int student_id = 12345;
char student_name[50] = "Alice Johnson";
float student_gpa = 3.75;
int student_age = 20;
char student_major[30] = "Electrical Engineering";

// If you have 100 students, you need 500 variables!

User-defined types solve this problem by allowing us to group related data together.

2. Structures (struct)

2.1 What is a Structure?

A structure is a user-defined data type that groups variables of different types under a single name. Think of it as creating your own custom data type.

Python vs C Comparison:

Python C
Uses classes or dictionaries Uses struct
student = {"name": "Alice", "age": 20} struct Student student;
Dynamic typing Static typing

2.2 Declaring a Structure

Basic Syntax:

struct structure_name {
    data_type member1;
    data_type member2;
    // ... more members
};

Example - Student Structure:

struct Student {
    int id;
    char name[50];
    float gpa;
    int age;
    char major[30];
};

Important Notes:

2.3 Creating Structure Variables

Method 1: After Structure Declaration

struct Student {
    int id;
    char name[50];
    float gpa;
};

// Create variables
struct Student student1;
struct Student student2, student3;

Method 2: During Structure Declaration

struct Student {
    int id;
    char name[50];
    float gpa;
} student1, student2;

Method 3: Anonymous Structure (less common)

struct {
    int id;
    char name[50];
    float gpa;
} student1, student2;

2.4 Initializing Structure Variables

Method 1: Member-by-Member Assignment

struct Student s1;
s1.id = 12345;
strcpy(s1.name, "Alice Johnson");  // Note: Use strcpy for strings
s1.gpa = 3.75;

Method 2: Initialization at Declaration

struct Student s1 = {12345, "Alice Johnson", 3.75};

Method 3: Designated Initializers (C99 and later)

struct Student s1 = {
    .id = 12345,
    .name = "Alice Johnson",
    .gpa = 3.75
};

Method 4: Partial Initialization

struct Student s1 = {12345};  // Only id is initialized, others are 0/NULL

2.5 Accessing Structure Members

Use the dot operator (.) to access structure members:

struct Student s1;

// Writing to members
s1.id = 12345;
s1.gpa = 3.75;
strcpy(s1.name, "Alice Johnson");

// Reading from members
printf("Student ID: %d\n", s1.id);
printf("Student Name: %s\n", s1.name);
printf("Student GPA: %.2f\n", s1.gpa);

2.6 Nested Structures

Structures can contain other structures as members:

struct Date {
    int day;
    int month;
    int year;
};

struct Student {
    int id;
    char name[50];
    float gpa;
    struct Date birthDate;  // Nested structure
};

// Usage
struct Student s1;
s1.id = 12345;
s1.birthDate.day = 15;
s1.birthDate.month = 8;
s1.birthDate.year = 2003;

printf("Birth Date: %d/%d/%d\n", 
       s1.birthDate.day, 
       s1.birthDate.month, 
       s1.birthDate.year);

2.7 Array of Structures

You can create arrays of structures to handle multiple records:

struct Student {
    int id;
    char name[50];
    float gpa;
};

// Array of 100 students
struct Student students[100];

// Accessing elements
students[0].id = 12345;
strcpy(students[0].name, "Alice");
students[0].gpa = 3.75;

// Loop through all students
for (int i = 0; i < 100; i++) {
    printf("Student %d: %s (GPA: %.2f)\n", 
           students[i].id, 
           students[i].name, 
           students[i].gpa);
}

2.8 Pointers to Structures

You can use pointers with structures:

struct Student s1 = {12345, "Alice", 3.75};
struct Student *ptr = &s1;

// Method 1: Using (*ptr).member
printf("ID: %d\n", (*ptr).id);

// Method 2: Using ptr->member (preferred)
printf("ID: %d\n", ptr->id);
printf("Name: %s\n", ptr->name);
printf("GPA: %.2f\n", ptr->gpa);

The Arrow Operator (->):

2.9 Structures and Functions

Passing by Value:

void printStudent(struct Student s) {
    printf("ID: %d\n", s.id);
    printf("Name: %s\n", s.name);
    printf("GPA: %.2f\n", s.gpa);
}

// Usage
struct Student s1 = {12345, "Alice", 3.75};
printStudent(s1);  // Entire structure is copied

Passing by Reference (Pointer):

void updateGPA(struct Student *s, float newGPA) {
    s->gpa = newGPA;
}

// Usage
struct Student s1 = {12345, "Alice", 3.75};
updateGPA(&s1, 3.85);  // Pass address of structure
printf("Updated GPA: %.2f\n", s1.gpa);  // Output: 3.85

Returning Structures from Functions:

struct Student createStudent(int id, char *name, float gpa) {
    struct Student s;
    s.id = id;
    strcpy(s.name, name);
    s.gpa = gpa;
    return s;
}

// Usage
struct Student s1 = createStudent(12345, "Alice", 3.75);

2.10 Practical Example: Student Database

#include <stdio.h>
#include <string.h>

struct Student {
    int id;
    char name[50];
    float gpa;
    int age;
};

// Function to input student data
void inputStudent(struct Student *s) {
    printf("Enter Student ID: ");
    scanf("%d", &s->id);
    
    printf("Enter Student Name: ");
    scanf(" %[^\n]", s->name);
    
    printf("Enter Student GPA: ");
    scanf("%f", &s->gpa);
    
    printf("Enter Student Age: ");
    scanf("%d", &s->age);
}

// Function to display student data
void displayStudent(struct Student s) {
    printf("\n--- Student Information ---\n");
    printf("ID: %d\n", s.id);
    printf("Name: %s\n", s.name);
    printf("GPA: %.2f\n", s.gpa);
    printf("Age: %d\n", s.age);
}

int main() {
    struct Student students[3];
    
    // Input data for 3 students
    for (int i = 0; i < 3; i++) {
        printf("\nEnter details for student %d:\n", i + 1);
        inputStudent(&students[i]);
    }
    
    // Display all students
    printf("\n\n=== All Students ===\n");
    for (int i = 0; i < 3; i++) {
        displayStudent(students[i]);
    }
    
    return 0;
}

3. Enumerations (enum)

3.1 What is an Enumeration?

An enumeration is a user-defined data type consisting of a set of named integer constants. Enums make code more readable by replacing "magic numbers" with meaningful names.

Python vs C Comparison:

Python C
No built-in enum (uses constants) Has enum keyword
RED = 0; GREEN = 1; BLUE = 2 enum Color {RED, GREEN, BLUE};

3.2 Declaring an Enum

Basic Syntax:

enum enum_name {
    constant1,
    constant2,
    constant3
};

Example:

enum Day {
    SUNDAY,     // 0
    MONDAY,     // 1
    TUESDAY,    // 2
    WEDNESDAY,  // 3
    THURSDAY,   // 4
    FRIDAY,     // 5
    SATURDAY    // 6
};

Key Points:

3.3 Custom Values in Enums

enum Status {
    SUCCESS = 1,
    FAILURE = 0,
    PENDING = -1
};

enum Month {
    JANUARY = 1,
    FEBRUARY,    // 2
    MARCH,       // 3
    APRIL,       // 4
    MAY,         // 5
    JUNE,        // 6
    JULY,        // 7
    AUGUST,      // 8
    SEPTEMBER,   // 9
    OCTOBER,     // 10
    NOVEMBER,    // 11
    DECEMBER     // 12
};

3.4 Using Enums

#include <stdio.h>

enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, 
    THURSDAY, FRIDAY, SATURDAY
};

int main() {
    enum Day today = WEDNESDAY;
    
    printf("Today is day number: %d\n", today);  // Output: 3
    
    if (today == WEDNESDAY) {
        printf("It's the middle of the week!\n");
    }
    
    // Using enum in switch statement
    switch (today) {
        case MONDAY:
            printf("Start of work week\n");
            break;
        case FRIDAY:
            printf("TGIF!\n");
            break;
        case SATURDAY:
        case SUNDAY:
            printf("Weekend!\n");
            break;
        default:
            printf("Regular work day\n");
    }
    
    return 0;
}

3.5 Enums with Structures

Combining enums with structures creates powerful data models:

enum Grade {
    GRADE_A = 90,
    GRADE_B = 80,
    GRADE_C = 70,
    GRADE_D = 60,
    GRADE_F = 0
};

enum StudentStatus {
    ACTIVE,
    GRADUATED,
    SUSPENDED,
    WITHDRAWN
};

struct Student {
    int id;
    char name[50];
    enum Grade grade;
    enum StudentStatus status;
};

int main() {
    struct Student s1 = {
        .id = 12345,
        .name = "Alice",
        .grade = GRADE_A,
        .status = ACTIVE
    };
    
    printf("Student: %s\n", s1.name);
    
    if (s1.status == ACTIVE) {
        printf("Status: Active Student\n");
    }
    
    if (s1.grade >= GRADE_B) {
        printf("Good performance!\n");
    }
    
    return 0;
}

3.6 Practical Example: Traffic Light System

#include <stdio.h>

enum TrafficLight {
    RED,
    YELLOW,
    GREEN
};

void displayLightAction(enum TrafficLight light) {
    switch (light) {
        case RED:
            printf("STOP! Red light is on.\n");
            break;
        case YELLOW:
            printf("CAUTION! Yellow light is on.\n");
            break;
        case GREEN:
            printf("GO! Green light is on.\n");
            break;
        default:
            printf("Invalid light state.\n");
    }
}

int main() {
    enum TrafficLight currentLight = RED;
    
    printf("Traffic Light Simulation:\n\n");
    
    for (int i = 0; i < 3; i++) {
        displayLightAction(currentLight);
        
        // Cycle through lights
        if (currentLight == RED) {
            currentLight = GREEN;
        } else if (currentLight == GREEN) {
            currentLight = YELLOW;
        } else {
            currentLight = RED;
        }
        
        printf("Waiting...\n\n");
    }
    
    return 0;
}

4. Type Definitions (typedef)

4.1 What is typedef?

typedef creates aliases (alternative names) for existing data types. It makes code more readable and easier to maintain.

Basic Syntax:

typedef existing_type new_name;

4.2 typedef with Basic Types

// Create aliases for basic types
typedef int Integer;
typedef float Real;
typedef char Character;

// Usage
Integer age = 25;
Real temperature = 36.5;
Character grade = 'A';

4.3 typedef with Structures

Without typedef:

struct Student {
    int id;
    char name[50];
    float gpa;
};

// Must always use "struct Student"
struct Student s1;
struct Student students[100];

With typedef - Method 1:

struct Student {
    int id;
    char name[50];
    float gpa;
};

typedef struct Student Student;

// Now can use just "Student"
Student s1;
Student students[100];

With typedef - Method 2 (Combined):

typedef struct Student {
    int id;
    char name[50];
    float gpa;
} Student;

// Usage
Student s1;
Student students[100];

With typedef - Method 3 (Anonymous struct):

typedef struct {
    int id;
    char name[50];
    float gpa;
} Student;

// Usage
Student s1;

4.4 typedef with Enums

typedef enum {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
} Day;

// Usage
Day today = WEDNESDAY;
Day weekend[2] = {SATURDAY, SUNDAY};

4.5 typedef with Pointers

typedef int* IntPtr;
typedef struct Student* StudentPtr;

// Usage
IntPtr p1, p2;  // Both are int pointers
StudentPtr sptr;  // Student pointer

4.6 typedef with Arrays

typedef int IntArray[10];
typedef char String[100];

// Usage
IntArray numbers;  // Same as: int numbers[10];
String name;       // Same as: char name[100];

4.7 Practical Example: Complex Data Types

#include <stdio.h>
#include <string.h>

// Define enumeration for course grades
typedef enum {
    GRADE_A = 4,
    GRADE_B = 3,
    GRADE_C = 2,
    GRADE_D = 1,
    GRADE_F = 0
} Grade;

// Define structure for a course
typedef struct {
    char code[10];
    char name[50];
    int credits;
    Grade grade;
} Course;

// Define structure for a student
typedef struct {
    int id;
    char name[50];
    Course courses[5];
    int numCourses;
    float gpa;
} Student;

// Function to calculate GPA
float calculateGPA(Student *s) {
    int totalCredits = 0;
    float totalPoints = 0.0;
    
    for (int i = 0; i < s->numCourses; i++) {
        totalCredits += s->courses[i].credits;
        totalPoints += s->courses[i].credits * s->courses[i].grade;
    }
    
    if (totalCredits == 0) return 0.0;
    return totalPoints / totalCredits;
}

int main() {
    Student student = {
        .id = 12345,
        .name = "Alice Johnson",
        .numCourses = 3
    };
    
    // Add courses
    strcpy(student.courses[0].code, "EE101");
    strcpy(student.courses[0].name, "Circuit Analysis");
    student.courses[0].credits = 3;
    student.courses[0].grade = GRADE_A;
    
    strcpy(student.courses[1].code, "MATH201");
    strcpy(student.courses[1].name, "Calculus II");
    student.courses[1].credits = 4;
    student.courses[1].grade = GRADE_B;
    
    strcpy(student.courses[2].code, "CS101");
    strcpy(student.courses[2].name, "Programming");
    student.courses[2].credits = 3;
    student.courses[2].grade = GRADE_A;
    
    // Calculate and display GPA
    student.gpa = calculateGPA(&student);
    
    printf("Student: %s (ID: %d)\n", student.name, student.id);
    printf("GPA: %.2f\n\n", student.gpa);
    
    printf("Courses:\n");
    for (int i = 0; i < student.numCourses; i++) {
        printf("  %s - %s (%d credits): Grade %d\n",
               student.courses[i].code,
               student.courses[i].name,
               student.courses[i].credits,
               student.courses[i].grade);
    }
    
    return 0;
}

5. File Input/Output

5.1 Why File I/O?

So far, all our programs lose their data when they terminate. File I/O allows programs to:

Python vs C File Operations:

Python C
f = open("file.txt", "r") FILE *f = fopen("file.txt", "r");
f.write("text") fprintf(f, "text");
content = f.read() fscanf(f, "%s", buffer);
f.close() fclose(f);

5.2 File Pointer

In C, files are accessed through file pointers of type FILE*:

FILE *filePointer;

The FILE type is defined in <stdio.h>.

5.3 Opening Files - fopen()

Function Signature:

FILE *fopen(const char *filename, const char *mode);

File Opening Modes:

Mode Description If File Exists If File Doesn't Exist
"r" Read only Opens file Returns NULL
"w" Write only Overwrites content Creates new file
"a" Append Appends to end Creates new file
"r+" Read and write Opens file Returns NULL
"w+" Read and write Overwrites content Creates new file
"a+" Read and append Opens file Creates new file
"rb" Read binary Opens file Returns NULL
"wb" Write binary Overwrites content Creates new file
"ab" Append binary Appends to end Creates new file

Example:

FILE *file;

// Open file for reading
file = fopen("data.txt", "r");

// Always check if file opened successfully
if (file == NULL) {
    printf("Error: Could not open file!\n");
    return 1;
}

// ... file operations ...

fclose(file);

5.4 Closing Files - fclose()

Always close files after use to:

int fclose(FILE *filePointer);

Returns:

Example:

FILE *file = fopen("data.txt", "r");
if (file != NULL) {
    // ... operations ...
    fclose(file);
}

5.5 Reading from Text Files

5.5.1 fscanf() - Formatted Input

Similar to scanf(), but reads from a file:

int fscanf(FILE *stream, const char *format, ...);

Example:

FILE *file = fopen("numbers.txt", "r");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

int num;
while (fscanf(file, "%d", &num) == 1) {
    printf("Read: %d\n", num);
}

fclose(file);

5.5.2 fgets() - Line Input

Reads a line from a file:

char *fgets(char *str, int n, FILE *stream);

Example:

FILE *file = fopen("text.txt", "r");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

char line[256];
while (fgets(line, sizeof(line), file) != NULL) {
    printf("%s", line);
}

fclose(file);

5.5.3 fgetc() - Character Input

Reads a single character:

int fgetc(FILE *stream);

Example:

FILE *file = fopen("text.txt", "r");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

int ch;
while ((ch = fgetc(file)) != EOF) {
    putchar(ch);
}

fclose(file);

5.6 Writing to Text Files

5.6.1 fprintf() - Formatted Output

Similar to printf(), but writes to a file:

int fprintf(FILE *stream, const char *format, ...);

Example:

FILE *file = fopen("output.txt", "w");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

fprintf(file, "Student ID: %d\n", 12345);
fprintf(file, "Name: %s\n", "Alice Johnson");
fprintf(file, "GPA: %.2f\n", 3.75);

fclose(file);

5.6.2 fputs() - String Output

Writes a string to a file:

int fputs(const char *str, FILE *stream);

Example:

FILE *file = fopen("output.txt", "w");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

fputs("Hello, World!\n", file);
fputs("This is a test.\n", file);

fclose(file);

5.6.3 fputc() - Character Output

Writes a single character:

int fputc(int char, FILE *stream);

Example:

FILE *file = fopen("output.txt", "w");
if (file == NULL) {
    printf("Error opening file!\n");
    return 1;
}

for (char ch = 'A'; ch <= 'Z'; ch++) {
    fputc(ch, file);
}

fclose(file);

5.7 File Position Functions

5.7.1 fseek() - Move File Pointer

int fseek(FILE *stream, long offset, int origin);

Origin values:

Example:

FILE *file = fopen("data.txt", "r");

// Move to beginning
fseek(file, 0, SEEK_SET);

// Move 10 bytes from current position
fseek(file, 10, SEEK_CUR);

// Move to end of file
fseek(file, 0, SEEK_END);

5.7.2 ftell() - Get Current Position

long ftell(FILE *stream);

Example:

FILE *file = fopen("data.txt", "r");
long position = ftell(file);
printf("Current position: %ld\n", position);

5.7.3 rewind() - Reset to Beginning

void rewind(FILE *stream);

Example:

FILE *file = fopen("data.txt", "r");

// Read some data...

rewind(file);  // Go back to beginning

5.8 Checking End of File - feof()

int feof(FILE *stream);

Returns non-zero if end of file is reached.

Example:

FILE *file = fopen("data.txt", "r");
char ch;

while (!feof(file)) {
    ch = fgetc(file);
    if (ch != EOF) {
        putchar(ch);
    }
}

fclose(file);

5.9 Practical Example: Student Records Manager

#include <stdio.h>
#include <string.h>

typedef struct {
    int id;
    char name[50];
    float gpa;
    int age;
} Student;

// Save student to file
void saveStudent(const char *filename, Student s) {
    FILE *file = fopen(filename, "a");  // Append mode
    if (file == NULL) {
        printf("Error opening file!\n");
        return;
    }
    
    fprintf(file, "%d,%s,%.2f,%d\n", s.id, s.name, s.gpa, s.age);
    fclose(file);
    
    printf("Student record saved successfully!\n");
}

// Load all students from file
void loadStudents(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        printf("No records found!\n");
        return;
    }
    
    Student s;
    printf("\n=== Student Records ===\n");
    
    while (fscanf(file, "%d,%49[^,],%f,%d\n", 
                  &s.id, s.name, &s.gpa, &s.age) == 4) {
        printf("ID: %d | Name: %s | GPA: %.2f | Age: %d\n",
               s.id, s.name, s.gpa, s.age);
    }
    
    fclose(file);
}

// Search for student by ID
int searchStudent(const char *filename, int searchID) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        printf("Error opening file!\n");
        return 0;
    }
    
    Student s;
    while (fscanf(file, "%d,%49[^,],%f,%d\n", 
                  &s.id, s.name, &s.gpa, &s.age) == 4) {
        if (s.id == searchID) {
            printf("\n--- Student Found ---\n");
            printf("ID: %d\n", s.id);
            printf("Name: %s\n", s.name);
            printf("GPA: %.2f\n", s.gpa);
            printf("Age: %d\n", s.age);
            fclose(file);
            return 1;
        }
    }
    
    fclose(file);
    printf("Student not found!\n");
    return 0;
}

int main() {
    int choice;
    Student s;
    
    do {
        printf("\n=== Student Management System ===\n");
        printf("1. Add Student\n");
        printf("2. View All Students\n");
        printf("3. Search Student by ID\n");
        printf("4. Exit\n");
        printf("Enter choice: ");
        scanf("%d", &choice);
        
        switch (choice) {
            case 1:
                printf("\nEnter Student ID: ");
                scanf("%d", &s.id);
                
                printf("Enter Student Name: ");
                scanf(" %[^\n]", s.name);
                
                printf("Enter Student GPA: ");
                scanf("%f", &s.gpa);
                
                printf("Enter Student Age: ");
                scanf("%d", &s.age);
                
                saveStudent("students.txt", s);
                break;
                
            case 2:
                loadStudents("students.txt");
                break;
                
            case 3: {
                int searchID;
                printf("\nEnter Student ID to search: ");
                scanf("%d", &searchID);
                searchStudent("students.txt", searchID);
                break;
            }
                
            case 4:
                printf("Exiting program...\n");
                break;
                
            default:
                printf("Invalid choice!\n");
        }
    } while (choice != 4);
    
    return 0;
}

5.10 Binary File Operations

Binary files store data in raw binary format, which is more efficient for storing structures.

5.10.1 Writing Binary Data - fwrite()

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

Parameters:

Example:

typedef struct {
    int id;
    char name[50];
    float gpa;
} Student;

Student s = {12345, "Alice Johnson", 3.75};

FILE *file = fopen("students.dat", "wb");
if (file != NULL) {
    fwrite(&s, sizeof(Student), 1, file);
    fclose(file);
}

5.10.2 Reading Binary Data - fread()

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

Parameters:

Example:

Student s;

FILE *file = fopen("students.dat", "rb");
if (file != NULL) {
    while (fread(&s, sizeof(Student), 1, file) == 1) {
        printf("ID: %d, Name: %s, GPA: %.2f\n", 
               s.id, s.name, s.gpa);
    }
    fclose(file);
}

5.10.3 Binary vs Text Files

Aspect Text Files Binary Files
Human Readable Yes No
Size Larger Smaller
Speed Slower Faster
Portability More portable Less portable
Precision May lose precision Full precision
Best for Configuration files, logs Large data, structures

5.11 Complete Binary File Example

#include <stdio.h>
#include <string.h>

typedef struct {
    int id;
    char name[50];
    float gpa;
    int age;
} Student;

// Save student to binary file
void saveStudentBinary(const char *filename, Student s) {
    FILE *file = fopen(filename, "ab");  // Append binary
    if (file == NULL) {
        printf("Error opening file!\n");
        return;
    }
    
    fwrite(&s, sizeof(Student), 1, file);
    fclose(file);
    
    printf("Student saved to binary file!\n");
}

// Load all students from binary file
void loadStudentsBinary(const char *filename) {
    FILE *file = fopen(filename, "rb");  // Read binary
    if (file == NULL) {
        printf("No records found!\n");
        return;
    }
    
    Student s;
    printf("\n=== Student Records (Binary) ===\n");
    
    while (fread(&s, sizeof(Student), 1, file) == 1) {
        printf("ID: %d | Name: %s | GPA: %.2f | Age: %d\n",
               s.id, s.name, s.gpa, s.age);
    }
    
    fclose(file);
}

// Count number of records in binary file
int countRecords(const char *filename) {
    FILE *file = fopen(filename, "rb");
    if (file == NULL) {
        return 0;
    }
    
    // Seek to end of file
    fseek(file, 0, SEEK_END);
    
    // Get file size
    long fileSize = ftell(file);
    
    fclose(file);
    
    // Calculate number of records
    return fileSize / sizeof(Student);
}

// Update student record by ID
int updateStudent(const char *filename, int id, Student newData) {
    FILE *file = fopen(filename, "rb+");  // Read and write binary
    if (file == NULL) {
        printf("Error opening file!\n");
        return 0;
    }
    
    Student s;
    int found = 0;
    long position = 0;
    
    // Search for student
    while (fread(&s, sizeof(Student), 1, file) == 1) {
        if (s.id == id) {
            // Move back to the position of this record
            fseek(file, position, SEEK_SET);
            
            // Write updated data
            fwrite(&newData, sizeof(Student), 1, file);
            
            found = 1;
            printf("Student record updated!\n");
            break;
        }
        position = ftell(file);
    }
    
    fclose(file);
    
    if (!found) {
        printf("Student ID %d not found!\n", id);
    }
    
    return found;
}

// Delete student record by ID
int deleteStudent(const char *filename, int id) {
    FILE *file = fopen(filename, "rb");
    FILE *temp = fopen("temp.dat", "wb");
    
    if (file == NULL || temp == NULL) {
        printf("Error opening files!\n");
        return 0;
    }
    
    Student s;
    int found = 0;
    
    // Copy all records except the one to delete
    while (fread(&s, sizeof(Student), 1, file) == 1) {
        if (s.id == id) {
            found = 1;
            continue;  // Skip this record
        }
        fwrite(&s, sizeof(Student), 1, temp);
    }
    
    fclose(file);
    fclose(temp);
    
    // Replace original file with temp file
    remove(filename);
    rename("temp.dat", filename);
    
    if (found) {
        printf("Student record deleted!\n");
    } else {
        printf("Student ID %d not found!\n", id);
    }
    
    return found;
}

int main() {
    int choice;
    Student s;
    
    do {
        printf("\n=== Student Management System (Binary Files) ===\n");
        printf("1. Add Student\n");
        printf("2. View All Students\n");
        printf("3. Count Records\n");
        printf("4. Update Student\n");
        printf("5. Delete Student\n");
        printf("6. Exit\n");
        printf("Enter choice: ");
        scanf("%d", &choice);
        
        switch (choice) {
            case 1:
                printf("\nEnter Student ID: ");
                scanf("%d", &s.id);
                
                printf("Enter Student Name: ");
                scanf(" %[^\n]", s.name);
                
                printf("Enter Student GPA: ");
                scanf("%f", &s.gpa);
                
                printf("Enter Student Age: ");
                scanf("%d", &s.age);
                
                saveStudentBinary("students.dat", s);
                break;
                
            case 2:
                loadStudentsBinary("students.dat");
                break;
                
            case 3: {
                int count = countRecords("students.dat");
                printf("\nTotal records: %d\n", count);
                break;
            }
                
            case 4: {
                int updateID;
                printf("\nEnter Student ID to update: ");
                scanf("%d", &updateID);
                
                printf("Enter new Student Name: ");
                scanf(" %[^\n]", s.name);
                
                printf("Enter new Student GPA: ");
                scanf("%f", &s.gpa);
                
                printf("Enter new Student Age: ");
                scanf("%d", &s.age);
                
                s.id = updateID;
                updateStudent("students.dat", updateID, s);
                break;
            }
                
            case 5: {
                int deleteID;
                printf("\nEnter Student ID to delete: ");
                scanf("%d", &deleteID);
                deleteStudent("students.dat", deleteID);
                break;
            }
                
            case 6:
                printf("Exiting program...\n");
                break;
                
            default:
                printf("Invalid choice!\n");
        }
    } while (choice != 6);
    
    return 0;
}