Built with Tutify.io

    AIStudyLabs

    Practical Example with Structures

    Practical Example with Structures

    Introduction

    In earlier lessons you learned how to declare, initialize, and use structures, as well as how they compare conceptually with unions. In this lesson, you will apply those ideas in a realistic mini‑project. We’ll model a simple student management scenario to practice:


    Designing a Practical Struct: Student Records

    We’ll build a small program that stores and prints information about students in a course. Each student has:

    Defining the Structure

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_NAME_LEN 50
    
    struct Student {
        int    id;
        char   name[MAX_NAME_LEN];
        float  grade;
        int    has_scholarship; // 0 = no, 1 = yes
    };
    
    // Function prototypes
    void print_student(struct Student s);
    void update_grade(struct Student *s, float new_grade);
    

    Here, struct Student groups related fields into one logical unit, making it easier to pass around complete student records instead of many separate variables.


    Initializing Structs with Designated Initializers

    Instead of relying on position-based initialization, designated initializers clearly show which field gets which value.

    int main(void) {
        struct Student s1 = {
            .id = 1001,
            .name = "Alice Johnson",
            .grade = 89.5f,
            .has_scholarship = 1
        };
    
        struct Student s2 = {0}; // zero-initialize all members
    
        // Later, we can set s2 fields explicitly
        s2.id = 1002;
        strcpy(s2.name, "Bob Smith");
        s2.grade = 72.0f;
        s2.has_scholarship = 0;
    
        print_student(s1);
        print_student(s2);
    
        update_grade(&s2, 78.0f);
        print_student(s2);
    
        return 0;
    }
    

    Explanation

    Output

    // Output:
    // ID: 1001, Name: Alice Johnson, Grade: 89.50, Scholarship: YES
    // ID: 1002, Name: Bob Smith, Grade: 72.00, Scholarship: NO
    // Updated grade for Bob Smith to 78.00
    // ID: 1002, Name: Bob Smith, Grade: 78.00, Scholarship: NO
    

    Accessing Structure Members with the Dot Operator

    To work with individual fields of a struct variable (not a pointer), use the dot (.) operator.

    void print_student(struct Student s) {
        printf("ID: %d, Name: %s, Grade: %.2f, Scholarship: %s\n",
               s.id,          // dot operator
               s.name,
               s.grade,
               s.has_scholarship ? "YES" : "NO");
    }
    

    Here, s.id, s.name, and s.grade access each member directly.

    When you have a pointer to a struct (like in update_grade), you will typically use ->, which is syntactic sugar for (*pointer).member.

    void update_grade(struct Student *s, float new_grade) {
        s->grade = new_grade; // same as (*s).grade = new_grade;
        printf("Updated grade for %s to %.2f\n", s->name, s->grade);
    }
    

    Passing Structures to Functions

    Passing by Value vs Passing by Pointer

    In print_student, we pass struct Student s by value. The function receives a copy of the struct:

    In update_grade, we pass struct Student *s by pointer:

    This combination is common: use by value when only reading data, and by pointer when updates are required.


    Arrays of Structures in a Practical Context

    Let’s extend the example to handle multiple students and compute an average grade.

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_NAME_LEN 50
    #define MAX_STUDENTS 3
    
    struct Student {
        int   id;
        char  name[MAX_NAME_LEN];
        float grade;
        int   has_scholarship;
    };
    
    void print_student(struct Student s) {
        printf("ID: %d, Name: %s, Grade: %.2f, Scholarship: %s\n",
               s.id, s.name, s.grade,
               s.has_scholarship ? "YES" : "NO");
    }
    
    float average_grade(const struct Student students[], int count) {
        float sum = 0.0f;
        for (int i = 0; i < count; i++) {
            sum += students[i].grade;  // dot via array element
        }
        return (count > 0) ? sum / count : 0.0f;
    }
    
    int main(void) {
        struct Student students[MAX_STUDENTS] = {
            [0] = { .id = 1001, .name = "Alice Johnson", .grade = 89.5f, .has_scholarship = 1 },
            [1] = { .id = 1002, .name = "Bob Smith",    .grade = 78.0f, .has_scholarship = 0 },
            [2] = { .id = 1003, .name = "Carol Lee",    .grade = 92.0f, .has_scholarship = 1 }
        };
    
        for (int i = 0; i < MAX_STUDENTS; i++) {
            print_student(students[i]);
        }
    
        float avg = average_grade(students, MAX_STUDENTS);
        printf("Average grade: %.2f\n", avg);
    
        return 0;
    }
    

    Explanation and Output

    // Output:
    // ID: 1001, Name: Alice Johnson, Grade: 89.50, Scholarship: YES
    // ID: 1002, Name: Bob Smith, Grade: 78.00, Scholarship: NO
    // ID: 1003, Name: Carol Lee, Grade: 92.00, Scholarship: YES
    // Average grade: 86.50
    

    Structures vs Unions: Memory Efficiency in Practice

    Imagine we also want to store extra info for each person in a system, but this extra info can be either a GPA or years of experience — never both at the same time.

    Using a struct:

    struct PersonStructInfo {
        float gpa;
        int   experience_years;
    };
    

    This always allocates memory for both gpa and experience_years, even if only one is relevant.

    Using a union:

    union PersonUnionInfo {
        float gpa;
        int   experience_years;
    };
    

    Here, gpa and experience_years share the same memory location. The size of the union is roughly the size of its largest member, making it more memory‑efficient when only one field is logically active at a time.

    In a real application, you’d typically combine these ideas with an additional flag to indicate which union member is valid:

    struct Person {
        char  name[50];
        int   is_student; // 1 = student, 0 = worker
        union {
            float gpa;            // used when is_student == 1
            int   experience_years; // used when is_student == 0
        } extra;
    };
    

    Here, the struct organizes related data, while the union optimizes memory for mutually exclusive values.


    Summary / Key Takeaways

    In this lesson Practical Example with Structures, you have learned:

    C Programming Fundamentals: From Zero to Confident C Coder — Structures and Unions in C

    Introduction to Programming and C

    Variables and Data Types

    Control Structures: Conditions and Loops

    Functions in C

    Arrays and Strings

    Working with Pointers

    File Handling in C

    Structures and Unions in C

    Project Development and Implementation

    Advanced C Concepts and Best Practices

    Tutorial Not Found