Module 6.1

C Structures

Learn how to create custom data types using structures in C. Group related variables together to represent real-world entities like students, employees, products, and more!

45 min read
Intermediate
Hands-on Examples
What You'll Learn
  • Define and declare structures
  • Access and modify structure members
  • Use typedef for cleaner code
  • Create arrays of structures
  • Pass structures to functions
Contents
01

Introduction to Structures

Structures are one of the most powerful features in C programming. They allow you to group different types of variables together under a single name, creating custom data types that represent real-world entities.

What is a Structure?

Imagine you're building a student management system. For each student, you need to store their name, age, grade, and student ID. Without structures, you'd need separate arrays for each piece of information - making it difficult to keep related data together and prone to synchronization errors.

A structure (or struct) solves this problem by letting you bundle all these related pieces of data into a single, cohesive unit. Think of it as creating a custom container that holds all the information about one entity.

Data Structure

Structure (struct)

A structure is a user-defined data type in C that groups together variables of different types under a single name. Each variable inside a structure is called a member or field.

Unlike arrays (which store multiple values of the same type), structures can hold different data types - integers, floats, characters, arrays, pointers, and even other structures.

Key characteristics: User-defined type, groups related data, members can be different types, contiguous memory allocation, accessed using dot (.) or arrow (->) operators.

Why Use Structures?

Structures are essential for organizing complex data in real-world applications. They provide a clean way to represent entities with multiple attributes and form the foundation for more advanced data structures like linked lists, trees, and graphs.

Data Organization

Group related variables together, making code more readable and maintainable. A "Student" structure keeps name, age, and grades in one place.

Code Reusability

Define a structure once, use it to create many instances. Create 100 students with the same structure template.

Clean Function Interfaces

Pass one structure instead of many separate variables. Functions become cleaner with fewer parameters.

Foundation for Data Structures

Structures are building blocks for linked lists, trees, graphs, and other complex data structures.

Structures vs Arrays

Both arrays and structures store multiple values, but they serve different purposes. Understanding when to use each is crucial for writing efficient C programs.

Feature Array Structure
Data Types Same type only Different types allowed
Access By index (numbers) By member name
Use Case Collection of similar items (e.g., 100 temperatures) Single entity with properties (e.g., one student's data)
Example int scores[5]; struct Student {...};

Your First Structure

Let's create a simple structure to represent a point in 2D space. A point has two coordinates: x and y. This is a classic example that demonstrates structure basics clearly.

// Define a structure for a 2D point
struct Point {
    int x;
    int y;
};

int main() {
    // Declare a structure variable
    struct Point p1;
    
    // Assign values to members
    p1.x = 10;
    p1.y = 20;
    
    // Access and print member values
    printf("Point: (%d, %d)\n", p1.x, p1.y);  // Point: (10, 20)
    
    return 0;
}
Note: The structure definition ends with a semicolon after the closing brace. This is a common source of errors for beginners - don't forget it!

Real-World Structure Example

Here's a more practical example - a structure to represent a student record with multiple data types including strings, integers, and floating-point numbers.

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

// Define the Student structure
struct Student {
    char name[50];
    int age;
    int student_id;
    float gpa;
};

int main() {
    // Declare and initialize a student
    struct Student s1;
    
    // Assign values using strcpy for strings
    strcpy(s1.name, "Priya Sharma");
    s1.age = 20;
    s1.student_id = 12345;
    s1.gpa = 3.85;
    
    // Display student information
    printf("Student Information:\n");
    printf("Name: %s\n", s1.name);        // Name: Priya Sharma
    printf("Age: %d\n", s1.age);          // Age: 20
    printf("ID: %d\n", s1.student_id);    // ID: 12345
    printf("GPA: %.2f\n", s1.gpa);        // GPA: 3.85
    
    return 0;
}
Important: You cannot use the assignment operator (=) directly with character arrays in C. Use strcpy() from <string.h> to copy strings into structure members.
02

Defining Structures

Learn the syntax for defining structures in C, including member declarations, initialization methods, and best practices for structure design.

Structure Definition Syntax

A structure definition tells the compiler what members the structure contains and their types. Think of it as creating a blueprint - it doesn't allocate memory until you declare a variable of that structure type.

// General syntax for structure definition
struct StructureName {
    data_type member1;
    data_type member2;
    data_type member3;
    // ... more members
};  // Don't forget this semicolon!

Here's a practical example with a structure representing a book:

// Define a Book structure
struct Book {
    char title[100];
    char author[50];
    int year;
    float price;
    int pages;
};

// Now you can declare Book variables
struct Book book1;
struct Book book2;

Declaration Methods

There are several ways to declare structure variables in C. Each has its use case, and understanding them will help you write cleaner code.

Method 1: Separate Definition and Declaration

This is the most common and recommended approach - define the structure first, then declare variables separately. This keeps your code organized and the structure can be reused easily.

// Step 1: Define the structure
struct Employee {
    char name[50];
    int emp_id;
    float salary;
};

// Step 2: Declare variables (can be anywhere after definition)
struct Employee emp1;
struct Employee emp2, emp3;
Method 2: Combined Definition and Declaration

You can declare variables immediately after the structure definition, before the semicolon. This is useful for quick prototypes but can make code harder to read.

// Define and declare in one statement
struct Rectangle {
    int width;
    int height;
} rect1, rect2;  // rect1 and rect2 are declared here

// You can still declare more later
struct Rectangle rect3;
Method 3: Anonymous Structure (No Tag)

You can create a structure without a name (tag). However, this means you can only declare variables at the time of definition - you cannot create more variables later.

// Anonymous structure - no tag name
struct {
    int x;
    int y;
} point1, point2;  // These are the ONLY variables you can create

// This would be an error - no name to reference:
// struct ??? point3;  // Can't create more!
Best Practice: Always give your structures a meaningful name (tag). This makes your code more readable and allows you to create new variables anywhere in your program.

Initializing Structures

Just like other variables, structure variables should be initialized before use. C provides several ways to initialize structures, from simple to more advanced.

Method 1: Member-by-Member Assignment

After declaring a structure variable, you can assign values to each member individually using the dot operator.

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

int main() {
    struct Student s1;
    
    // Initialize each member separately
    strcpy(s1.name, "Rahul Kumar");
    s1.age = 21;
    s1.gpa = 3.75;
    
    return 0;
}

Method 2: Initializer List (at Declaration)

The cleanest approach is to initialize all members at the time of declaration using curly braces. Values must be in the same order as the members are defined.

struct Point {
    int x;
    int y;
};

int main() {
    // Initialize at declaration - values in order
    struct Point p1 = {10, 20};  // x=10, y=20
    
    // Partial initialization - remaining members set to 0
    struct Point p2 = {5};       // x=5, y=0
    
    // All zeros
    struct Point origin = {0};   // x=0, y=0
    
    printf("p1: (%d, %d)\n", p1.x, p1.y);  // p1: (10, 20)
    printf("p2: (%d, %d)\n", p2.x, p2.y);  // p2: (5, 0)
    
    return 0;
}

Method 3: Designated Initializers (C99)

C99 introduced designated initializers, which let you specify member names explicitly. This is more readable and allows initialization in any order.

struct Product {
    char name[30];
    int quantity;
    float price;
};

int main() {
    // Designated initializers - specify by name
    struct Product item = {
        .name = "Laptop",
        .price = 999.99,
        .quantity = 50
    };
    
    // Mix order as needed
    struct Product item2 = {
        .quantity = 100,
        .name = "Mouse",
        .price = 29.99
    };
    
    printf("%s: $%.2f (x%d)\n", item.name, item.price, item.quantity);
    // Laptop: $999.99 (x50)
    
    return 0;
}
Pro Tip: Designated initializers make code self-documenting and protect against bugs when structure members are reordered. They're the preferred method in modern C code.

Structure Memory Layout

Understanding how structures are stored in memory helps you write more efficient code. Structure members are stored in contiguous memory locations in the order they are declared.

#include <stdio.h>

struct Example {
    char a;    // 1 byte
    int b;     // 4 bytes
    char c;    // 1 byte
};

int main() {
    struct Example ex;
    
    printf("Size of struct: %zu bytes\n", sizeof(ex));
    printf("Address of a: %p\n", (void*)&ex.a);
    printf("Address of b: %p\n", (void*)&ex.b);
    printf("Address of c: %p\n", (void*)&ex.c);
    
    return 0;
}
Structure Padding: The compiler may add padding bytes between members for alignment purposes. The actual size of a structure may be larger than the sum of its member sizes. This is why the structure above might be 12 bytes instead of 6!

Practice Questions: Defining Structures

Task: Define a structure called Rectangle with members for width and height (both integers). Create a variable and initialize it with width=10, height=5. Print the area.

Show Solution
#include <stdio.h>

struct Rectangle {
    int width;
    int height;
};

int main() {
    struct Rectangle rect = {10, 5};
    int area = rect.width * rect.height;
    
    printf("Width: %d, Height: %d\n", rect.width, rect.height);
    printf("Area: %d\n", area);  // Area: 50
    
    return 0;
}

Task: Define a Book structure with title (char array), author (char array), year (int), and price (float). Use designated initializers to create a book and print its details.

Show Solution
#include <stdio.h>

struct Book {
    char title[100];
    char author[50];
    int year;
    float price;
};

int main() {
    struct Book book = {
        .title = "The C Programming Language",
        .author = "Kernighan and Ritchie",
        .year = 1978,
        .price = 45.99
    };
    
    printf("Title: %s\n", book.title);
    printf("Author: %s\n", book.author);
    printf("Year: %d\n", book.year);
    printf("Price: $%.2f\n", book.price);
    
    return 0;
}

Task: Create a structure with members: char (1 byte), double (8 bytes), char (1 byte). Predict and verify the structure size. Explain why it differs from the simple sum.

Show Solution
#include <stdio.h>

struct Padded {
    char a;      // 1 byte + 7 padding
    double b;    // 8 bytes
    char c;      // 1 byte + 7 padding
};

struct Optimized {
    double b;    // 8 bytes
    char a;      // 1 byte
    char c;      // 1 byte + 6 padding
};

int main() {
    printf("Padded size: %zu bytes\n", sizeof(struct Padded));
    // Likely 24 bytes due to alignment
    
    printf("Optimized size: %zu bytes\n", sizeof(struct Optimized));
    // Likely 16 bytes - better ordering
    
    // Simple sum would be: 1 + 8 + 1 = 10 bytes
    // But alignment requires doubles to be at 8-byte boundaries
    
    return 0;
}
03

Accessing Structure Members

Master the dot operator and arrow operator for accessing and modifying structure members, including working with structure pointers.

The Dot Operator (.)

The dot operator is used to access members of a structure variable directly. It's the most common way to read and write structure member values. The syntax is variable.member.

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

struct Car {
    char brand[30];
    char model[30];
    int year;
    float price;
};

int main() {
    struct Car car1;
    
    // Using dot operator to SET values
    strcpy(car1.brand, "Toyota");
    strcpy(car1.model, "Camry");
    car1.year = 2024;
    car1.price = 28500.00;
    
    // Using dot operator to GET values
    printf("Brand: %s\n", car1.brand);    // Brand: Toyota
    printf("Model: %s\n", car1.model);    // Model: Camry
    printf("Year: %d\n", car1.year);      // Year: 2024
    printf("Price: $%.2f\n", car1.price); // Price: $28500.00
    
    // Modifying a member
    car1.price = 26000.00;  // Apply discount
    printf("New Price: $%.2f\n", car1.price);  // New Price: $26000.00
    
    return 0;
}

Using Members in Expressions

Structure members can be used anywhere you'd use a regular variable - in calculations, comparisons, function arguments, and more.

struct Rectangle {
    int width;
    int height;
};

int main() {
    struct Rectangle r1 = {10, 5};
    struct Rectangle r2 = {8, 7};
    
    // Using members in calculations
    int area1 = r1.width * r1.height;
    int area2 = r2.width * r2.height;
    
    printf("Rectangle 1 Area: %d\n", area1);  // 50
    printf("Rectangle 2 Area: %d\n", area2);  // 56
    
    // Using members in comparisons
    if (r1.width > r2.width) {
        printf("r1 is wider\n");
    } else {
        printf("r2 is wider or equal\n");
    }
    
    // Increment a member
    r1.width++;
    printf("New r1 width: %d\n", r1.width);  // 11
    
    return 0;
}

The Arrow Operator (->)

When you have a pointer to a structure, you use the arrow operator (->) to access its members. This is equivalent to dereferencing the pointer and then using the dot operator: ptr->member is the same as (*ptr).member.

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

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

int main() {
    struct Student s1 = {"Ananya Singh", 22, 3.9};
    
    // Create a pointer to the structure
    struct Student *ptr = &s1;
    
    // Access members using arrow operator
    printf("Name: %s\n", ptr->name);   // Name: Ananya Singh
    printf("Age: %d\n", ptr->age);     // Age: 22
    printf("GPA: %.1f\n", ptr->gpa);   // GPA: 3.9
    
    // Modify through pointer
    ptr->age = 23;
    ptr->gpa = 4.0;
    
    printf("\nAfter modification:\n");
    printf("Age: %d\n", ptr->age);     // Age: 23
    printf("GPA: %.1f\n", ptr->gpa);   // GPA: 4.0
    
    return 0;
}

Arrow vs Dot: When to Use Which

Choosing between dot and arrow operators is simple once you understand the rule:

Use Dot (.)

When you have a structure variable directly.

struct Point p1;
p1.x = 10;  // Direct access
Use Arrow (->)

When you have a pointer to a structure.

struct Point *ptr;
ptr->x = 10;  // Pointer access
struct Point {
    int x;
    int y;
};

int main() {
    struct Point p1 = {10, 20};
    struct Point *ptr = &p1;
    
    // These are all equivalent ways to access x:
    printf("%d\n", p1.x);        // Using dot with variable
    printf("%d\n", ptr->x);      // Using arrow with pointer
    printf("%d\n", (*ptr).x);    // Dereference + dot (arrow equivalent)
    
    // All print: 10
    
    return 0;
}
Common Error: *ptr.x is WRONG! Due to operator precedence, this tries to dereference ptr.x (which doesn't exist). Always use parentheses: (*ptr).x or better yet, use the arrow operator: ptr->x.

Copying Structures

Unlike arrays, you can copy entire structures using the simple assignment operator. This creates a complete copy of all members - a "shallow copy."

struct Point {
    int x;
    int y;
};

int main() {
    struct Point p1 = {10, 20};
    struct Point p2;
    
    // Copy entire structure
    p2 = p1;
    
    printf("p1: (%d, %d)\n", p1.x, p1.y);  // p1: (10, 20)
    printf("p2: (%d, %d)\n", p2.x, p2.y);  // p2: (10, 20)
    
    // Modifying p2 doesn't affect p1
    p2.x = 100;
    
    printf("\nAfter modifying p2:\n");
    printf("p1: (%d, %d)\n", p1.x, p1.y);  // p1: (10, 20) - unchanged
    printf("p2: (%d, %d)\n", p2.x, p2.y);  // p2: (100, 20)
    
    return 0;
}
Note: Structure assignment works for structures containing arrays too - the entire array is copied. However, if a structure contains pointers, only the pointer value is copied (not the data it points to). This is called a "shallow copy."

Comparing Structures

Unlike the assignment operator, you cannot use == or != to compare entire structures in C. You must compare member by member.

struct Point {
    int x;
    int y;
};

// Function to compare two Points
int points_equal(struct Point p1, struct Point p2) {
    return (p1.x == p2.x) && (p1.y == p2.y);
}

int main() {
    struct Point a = {10, 20};
    struct Point b = {10, 20};
    struct Point c = {5, 15};
    
    // This is NOT allowed in C:
    // if (a == b)  // Compilation error!
    
    // Compare member by member:
    if (a.x == b.x && a.y == b.y) {
        printf("a and b are equal\n");  // This prints
    }
    
    // Or use a function:
    if (points_equal(a, b)) {
        printf("a and b are equal (function)\n");  // This prints
    }
    
    if (!points_equal(a, c)) {
        printf("a and c are different\n");  // This prints
    }
    
    return 0;
}

Practice Questions: Accessing Members

Task: Create a Counter structure with a single int count member. Create a pointer to it and increment the count 5 times using the arrow operator. Print the final value.

Show Solution
#include <stdio.h>

struct Counter {
    int count;
};

int main() {
    struct Counter c = {0};
    struct Counter *ptr = &c;
    
    // Increment 5 times using arrow operator
    ptr->count++;
    ptr->count++;
    ptr->count++;
    ptr->count++;
    ptr->count++;
    
    printf("Final count: %d\n", ptr->count);  // Final count: 5
    
    return 0;
}

Task: Define a Rectangle structure with width and height. Write a function that takes two Rectangle pointers and returns 1 if they have equal dimensions, 0 otherwise.

Show Solution
#include <stdio.h>

struct Rectangle {
    int width;
    int height;
};

int rectangles_equal(struct Rectangle *r1, struct Rectangle *r2) {
    return (r1->width == r2->width) && (r1->height == r2->height);
}

int main() {
    struct Rectangle a = {10, 5};
    struct Rectangle b = {10, 5};
    struct Rectangle c = {8, 6};
    
    if (rectangles_equal(&a, &b)) {
        printf("a and b are equal\n");  // This prints
    }
    
    if (!rectangles_equal(&a, &c)) {
        printf("a and c are different\n");  // This prints
    }
    
    return 0;
}

Task: Write a function that takes two Point pointers and swaps their values. Test it by creating two points and displaying their values before and after the swap.

Show Solution
#include <stdio.h>

struct Point {
    int x;
    int y;
};

void swap_points(struct Point *p1, struct Point *p2) {
    struct Point temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

int main() {
    struct Point a = {10, 20};
    struct Point b = {30, 40};
    
    printf("Before swap:\n");
    printf("a: (%d, %d)\n", a.x, a.y);  // a: (10, 20)
    printf("b: (%d, %d)\n", b.x, b.y);  // b: (30, 40)
    
    swap_points(&a, &b);
    
    printf("\nAfter swap:\n");
    printf("a: (%d, %d)\n", a.x, a.y);  // a: (30, 40)
    printf("b: (%d, %d)\n", b.x, b.y);  // b: (10, 20)
    
    return 0;
}
04

Typedef with Structures

Simplify your code using typedef to create type aliases for structures, eliminating the need to write "struct" repeatedly.

Why Use typedef with Structures?

In C, every time you declare a structure variable, you must include the struct keyword. This can become tedious, especially when you use the structure type frequently. The typedef keyword creates an alias that lets you skip the struct keyword.

// Without typedef - must use "struct" every time
struct Student {
    char name[50];
    int age;
};

struct Student s1;         // Need "struct" keyword
struct Student s2;         // Need "struct" keyword
struct Student *ptr;       // Need "struct" keyword

// With typedef - cleaner declarations
typedef struct {
    char name[50];
    int age;
} Student;

Student s1;                // No "struct" needed!
Student s2;                // Much cleaner
Student *ptr;              // Easier to read

Two Ways to Use typedef

Method 1: typedef After Definition

Define the structure first, then create a typedef alias for it.

// Step 1: Define the structure
struct point {
    int x;
    int y;
};

// Step 2: Create a typedef alias
typedef struct point Point;

// Now you can use either:
struct point p1;  // Old way still works
Point p2;         // New clean way
Method 2: Combined Definition (Recommended)

The most common approach combines the structure definition and typedef in one statement. You can optionally include a tag name or leave it anonymous.

// With tag name (useful for self-referential structures)
typedef struct node {
    int data;
    struct node *next;  // Can reference itself using tag
} Node;

// Without tag name (simpler, but can't self-reference)
typedef struct {
    char name[50];
    int age;
    float salary;
} Employee;

int main() {
    Node n1;
    Employee emp1;
    
    emp1.age = 30;
    printf("Age: %d\n", emp1.age);  // Age: 30
    
    return 0;
}
Self-Referential Structures: When a structure contains a pointer to its own type (like linked list nodes), you need a tag name so you can reference it inside the definition. typedef struct node { ... struct node *next; } Node;

Complete Example with typedef

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

// Define types using typedef
typedef struct {
    char brand[30];
    char model[30];
    int year;
} Car;

typedef struct {
    char name[50];
    int age;
    Car car;  // Can use Car type without "struct"
} Person;

int main() {
    // Clean declarations without "struct"
    Person p1;
    
    strcpy(p1.name, "Vikram Patel");
    p1.age = 35;
    strcpy(p1.car.brand, "Honda");
    strcpy(p1.car.model, "Civic");
    p1.car.year = 2023;
    
    printf("%s, age %d, drives a %d %s %s\n",
           p1.name, p1.age, 
           p1.car.year, p1.car.brand, p1.car.model);
    // Vikram Patel, age 35, drives a 2023 Honda Civic
    
    return 0;
}

typedef vs #define

While both can create shortcuts, typedef is preferred for type aliases because it's processed by the compiler (not the preprocessor) and provides better type checking.

Feature typedef #define
Processing Compiler Preprocessor
Type Safety Yes No (text substitution)
Scope Follows C scoping rules Global from point of definition
Debugging Better error messages Harder to debug

Practice Questions: Typedef

Given:

struct rectangle {
    int width;
    int height;
};
struct rectangle r1;

Task: Rewrite using typedef so you can declare variables as Rectangle r1;

Show Solution
typedef struct {
    int width;
    int height;
} Rectangle;

Rectangle r1;  // Clean declaration

Task: Create a typedef for a linked list node structure that contains an integer data field and a pointer to the next node.

Show Solution
typedef struct node {
    int data;
    struct node *next;  // Must use "struct node" here
} Node;

int main() {
    Node n1, n2;
    
    n1.data = 10;
    n1.next = &n2;
    
    n2.data = 20;
    n2.next = NULL;
    
    // Traverse and print
    Node *current = &n1;
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");  // 10 -> 20 -> NULL
    
    return 0;
}
05

Arrays of Structures

Store and manage collections of structured data by creating arrays of structures - essential for handling databases, inventories, and record-keeping systems.

Creating Arrays of Structures

Just like you can create arrays of integers or floats, you can create arrays where each element is a complete structure. This is incredibly useful for managing collections of records - like a list of students, products in inventory, or employees in a company.

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

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

int main() {
    // Declare an array of 3 Student structures
    Student class[3];
    
    // Initialize first student
    strcpy(class[0].name, "Alice Johnson");
    class[0].age = 20;
    class[0].gpa = 3.8;
    
    // Initialize second student
    strcpy(class[1].name, "Bob Smith");
    class[1].age = 22;
    class[1].gpa = 3.5;
    
    // Initialize third student
    strcpy(class[2].name, "Carol Davis");
    class[2].age = 21;
    class[2].gpa = 3.9;
    
    // Print all students
    for (int i = 0; i < 3; i++) {
        printf("%s, Age: %d, GPA: %.2f\n", 
               class[i].name, class[i].age, class[i].gpa);
    }
    
    return 0;
}

Initializing at Declaration

You can initialize an array of structures at the time of declaration using nested braces. Each inner set of braces initializes one structure element.

typedef struct {
    char name[30];
    float price;
    int quantity;
} Product;

int main() {
    // Initialize array at declaration
    Product inventory[4] = {
        {"Laptop", 999.99, 50},
        {"Mouse", 29.99, 200},
        {"Keyboard", 79.99, 150},
        {"Monitor", 349.99, 75}
    };
    
    // Calculate total inventory value
    float total = 0;
    for (int i = 0; i < 4; i++) {
        total += inventory[i].price * inventory[i].quantity;
        printf("%-12s: $%7.2f x %3d = $%10.2f\n",
               inventory[i].name,
               inventory[i].price,
               inventory[i].quantity,
               inventory[i].price * inventory[i].quantity);
    }
    
    printf("\nTotal Inventory Value: $%.2f\n", total);
    
    return 0;
}

Using Designated Initializers

For clearer code, use designated initializers with arrays of structures. This makes it explicit which value goes to which member.

typedef struct {
    int id;
    char name[30];
    float salary;
} Employee;

int main() {
    Employee team[3] = {
        {.id = 101, .name = "Priya Sharma", .salary = 75000.00},
        {.id = 102, .name = "Rahul Patel", .salary = 68000.00},
        {.id = 103, .name = "Anita Gupta", .salary = 82000.00}
    };
    
    printf("Employee Directory:\n");
    printf("%-5s %-20s %12s\n", "ID", "Name", "Salary");
    printf("-------------------------------------------\n");
    
    for (int i = 0; i < 3; i++) {
        printf("%-5d %-20s $%10.2f\n",
               team[i].id, team[i].name, team[i].salary);
    }
    
    return 0;
}

Searching and Sorting Structure Arrays

With arrays of structures, you often need to search for specific records or sort them based on a particular member. Here's how to implement these common operations.

Linear Search

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

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

// Search for student by ID, return index or -1 if not found
int find_student(Student arr[], int size, int target_id) {
    for (int i = 0; i < size; i++) {
        if (arr[i].id == target_id) {
            return i;  // Found at index i
        }
    }
    return -1;  // Not found
}

int main() {
    Student students[4] = {
        {101, "Alice"},
        {102, "Bob"},
        {103, "Carol"},
        {104, "David"}
    };
    
    int search_id = 103;
    int index = find_student(students, 4, search_id);
    
    if (index != -1) {
        printf("Found: %s (ID: %d)\n", 
               students[index].name, students[index].id);
    } else {
        printf("Student with ID %d not found\n", search_id);
    }
    
    return 0;
}

Bubble Sort by a Member

typedef struct {
    char name[30];
    float gpa;
} Student;

// Sort students by GPA in descending order
void sort_by_gpa(Student arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j].gpa < arr[j + 1].gpa) {
                // Swap entire structures
                Student temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    Student class[4] = {
        {"Alice", 3.5},
        {"Bob", 3.9},
        {"Carol", 3.2},
        {"David", 3.7}
    };
    
    printf("Before sorting:\n");
    for (int i = 0; i < 4; i++) {
        printf("%s: %.2f\n", class[i].name, class[i].gpa);
    }
    
    sort_by_gpa(class, 4);
    
    printf("\nAfter sorting by GPA (descending):\n");
    for (int i = 0; i < 4; i++) {
        printf("%s: %.2f\n", class[i].name, class[i].gpa);
    }
    
    return 0;
}

Practice Questions: Arrays of Structures

Task: Given an array of Product structures (name, price), write code to find and print the most expensive product.

Show Solution
typedef struct {
    char name[30];
    float price;
} Product;

int main() {
    Product products[4] = {
        {"Laptop", 999.99},
        {"Phone", 799.99},
        {"Tablet", 549.99},
        {"Watch", 399.99}
    };
    
    int max_idx = 0;
    for (int i = 1; i < 4; i++) {
        if (products[i].price > products[max_idx].price) {
            max_idx = i;
        }
    }
    
    printf("Most expensive: %s at $%.2f\n",
           products[max_idx].name, products[max_idx].price);
    
    return 0;
}

Task: Create an array of 5 Student structures (name, gpa). Calculate and print the average GPA, and list students with above-average GPA.

Show Solution
typedef struct {
    char name[30];
    float gpa;
} Student;

int main() {
    Student class[5] = {
        {"Alice", 3.8}, {"Bob", 3.2}, {"Carol", 3.9},
        {"David", 3.5}, {"Eve", 3.7}
    };
    
    // Calculate average
    float sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += class[i].gpa;
    }
    float average = sum / 5;
    
    printf("Class Average GPA: %.2f\n\n", average);
    printf("Students above average:\n");
    
    for (int i = 0; i < 5; i++) {
        if (class[i].gpa > average) {
            printf("  %s: %.2f\n", class[i].name, class[i].gpa);
        }
    }
    
    return 0;
}

Task: Create an array of Employee structures and sort them alphabetically by name using strcmp().

Show Solution
#include <string.h>

typedef struct {
    int id;
    char name[30];
} Employee;

void sort_by_name(Employee arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (strcmp(arr[j].name, arr[j + 1].name) > 0) {
                Employee temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    Employee team[4] = {
        {101, "Zara"}, {102, "Alice"},
        {103, "Mike"}, {104, "Beth"}
    };
    
    sort_by_name(team, 4);
    
    printf("Sorted by name:\n");
    for (int i = 0; i < 4; i++) {
        printf("%d: %s\n", team[i].id, team[i].name);
    }
    // Output: Alice, Beth, Mike, Zara
    
    return 0;
}
06

Structures and Functions

Learn how to pass structures to functions by value and by reference, and how to return structures from functions for modular code design.

Passing Structures by Value

When you pass a structure to a function by value, a complete copy of the structure is made. Changes inside the function do NOT affect the original structure.

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

// Function receives a COPY of the structure
void move_point(Point p, int dx, int dy) {
    p.x += dx;  // Modifies the copy
    p.y += dy;
    printf("Inside function: (%d, %d)\n", p.x, p.y);
}

int main() {
    Point p1 = {10, 20};
    
    printf("Before: (%d, %d)\n", p1.x, p1.y);  // (10, 20)
    
    move_point(p1, 5, 5);  // Pass by value
    
    printf("After: (%d, %d)\n", p1.x, p1.y);   // (10, 20) - unchanged!
    
    return 0;
}
Performance Note: Passing large structures by value is inefficient because the entire structure must be copied. For large structures, prefer passing by pointer.

Passing Structures by Reference (Pointer)

To modify the original structure, pass a pointer to it. This is more efficient for large structures and allows the function to modify the original data.

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

// Function receives a POINTER to the structure
void move_point(Point *p, int dx, int dy) {
    p->x += dx;  // Modifies the original
    p->y += dy;
    printf("Inside function: (%d, %d)\n", p->x, p->y);
}

int main() {
    Point p1 = {10, 20};
    
    printf("Before: (%d, %d)\n", p1.x, p1.y);  // (10, 20)
    
    move_point(&p1, 5, 5);  // Pass pointer (address)
    
    printf("After: (%d, %d)\n", p1.x, p1.y);   // (15, 25) - modified!
    
    return 0;
}

When to Use Which Approach

Pass by Value
  • Small structures (few bytes)
  • Function should NOT modify original
  • Need a working copy inside function
  • Simpler, safer code
Pass by Pointer
  • Large structures (many members)
  • Function needs to modify original
  • Better performance (no copying)
  • Required for dynamic allocation

Read-Only Access with const

When you want the efficiency of passing by pointer but don't want the function to modify the structure, use const. This tells the compiler to prevent any modifications.

typedef struct {
    char name[50];
    float balance;
} Account;

// const pointer - can read but not modify
void print_account(const Account *acc) {
    printf("Name: %s\n", acc->name);
    printf("Balance: $%.2f\n", acc->balance);
    
    // This would cause a compilation error:
    // acc->balance = 0;  // Error: cannot modify const
}

int main() {
    Account a1 = {"John Doe", 5000.00};
    print_account(&a1);  // Safe - cannot modify
    return 0;
}
Best Practice: Use const with pointer parameters when the function should only read the structure. This documents your intent and helps catch bugs.

Returning Structures from Functions

Functions can return entire structures. This is useful when creating new structures or when a function needs to return multiple related values.

#include <stdio.h>

typedef struct {
    int x;
    int y;
} Point;

// Function returns a structure
Point create_point(int x, int y) {
    Point p;
    p.x = x;
    p.y = y;
    return p;
}

// Function returns a modified structure
Point add_points(Point a, Point b) {
    Point result;
    result.x = a.x + b.x;
    result.y = a.y + b.y;
    return result;
}

int main() {
    Point p1 = create_point(10, 20);
    Point p2 = create_point(5, 15);
    Point sum = add_points(p1, p2);
    
    printf("p1: (%d, %d)\n", p1.x, p1.y);    // (10, 20)
    printf("p2: (%d, %d)\n", p2.x, p2.y);    // (5, 15)
    printf("sum: (%d, %d)\n", sum.x, sum.y); // (15, 35)
    
    return 0;
}

Complete Example: Student Record System

Here's a practical example combining all the concepts - creating, modifying, and displaying student records using functions.

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

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

// Create a new student (returns structure)
Student create_student(int id, const char *name, float gpa) {
    Student s;
    s.id = id;
    strncpy(s.name, name, 49);
    s.name[49] = '\0';  // Ensure null termination
    s.gpa = gpa;
    return s;
}

// Display student info (const pointer - read only)
void print_student(const Student *s) {
    printf("ID: %d | Name: %-20s | GPA: %.2f\n",
           s->id, s->name, s->gpa);
}

// Update GPA (pointer - modifies original)
void update_gpa(Student *s, float new_gpa) {
    if (new_gpa >= 0.0 && new_gpa <= 4.0) {
        s->gpa = new_gpa;
        printf("GPA updated successfully.\n");
    } else {
        printf("Error: Invalid GPA value.\n");
    }
}

int main() {
    // Create students
    Student s1 = create_student(101, "Priya Sharma", 3.7);
    Student s2 = create_student(102, "Rahul Patel", 3.5);
    
    printf("Student Records:\n");
    printf("-------------------------------------------\n");
    print_student(&s1);
    print_student(&s2);
    
    // Update a student's GPA
    printf("\nUpdating Rahul's GPA to 3.8...\n");
    update_gpa(&s2, 3.8);
    
    printf("\nUpdated Records:\n");
    printf("-------------------------------------------\n");
    print_student(&s1);
    print_student(&s2);
    
    return 0;
}

Practice Questions: Structures and Functions

Task: Create a Rectangle structure and write a function that takes a Rectangle and returns its area.

Show Solution
typedef struct {
    int width;
    int height;
} Rectangle;

int get_area(Rectangle r) {
    return r.width * r.height;
}

int main() {
    Rectangle r1 = {10, 5};
    printf("Area: %d\n", get_area(r1));  // Area: 50
    return 0;
}

Task: Write a function that takes a Point pointer and scales both x and y by a given factor. Modify the original point.

Show Solution
typedef struct {
    int x;
    int y;
} Point;

void scale_point(Point *p, int factor) {
    p->x *= factor;
    p->y *= factor;
}

int main() {
    Point p = {5, 10};
    
    printf("Before: (%d, %d)\n", p.x, p.y);  // (5, 10)
    scale_point(&p, 3);
    printf("After: (%d, %d)\n", p.x, p.y);   // (15, 30)
    
    return 0;
}

Task: Write a function that takes an array of Student structures and its size, then returns a pointer to the student with the highest GPA.

Show Solution
typedef struct {
    char name[30];
    float gpa;
} Student;

Student* find_top_student(Student arr[], int size) {
    if (size == 0) return NULL;
    
    Student *top = &arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i].gpa > top->gpa) {
            top = &arr[i];
        }
    }
    return top;
}

int main() {
    Student class[4] = {
        {"Alice", 3.5}, {"Bob", 3.9},
        {"Carol", 3.7}, {"David", 3.8}
    };
    
    Student *top = find_top_student(class, 4);
    
    if (top != NULL) {
        printf("Top student: %s with GPA %.2f\n",
               top->name, top->gpa);
    }
    // Output: Top student: Bob with GPA 3.90
    
    return 0;
}

Interactive Demo: Structure Memory Visualizer

Explore how structures are stored in memory. Define structure members and see how the memory layout is calculated with proper alignment.

Add Structure Members
Memory Layout

Click "Visualize Memory" to see the layout

Generated Code
struct Student {
    int id;
    float gpa;
};

Interactive: Structure vs Array Challenge

Test your understanding by identifying whether each scenario is better suited for structures or arrays.

Scenario 1 of 5

Storing information about a book (title, author, price, ISBN)

Score: 0/5
07

Key Takeaways

Custom Data Types

Structures let you create custom data types by grouping related variables of different types under a single name

Dot Operator

Use the dot operator (.) to access structure members directly from a structure variable

Arrow Operator

Use the arrow operator (->) to access structure members through a pointer to a structure

Typedef Simplification

Use typedef to create shorter, cleaner type names for structures without the struct keyword

Arrays of Structures

Create arrays of structures to manage collections of records like student databases or inventories

Pass by Reference

Pass structures by pointer to functions for efficiency and to allow modifications to the original data

08

Test Your Knowledge

Put your understanding of C structures to the test with these quiz questions!

1 Which keyword is used to define a structure in C?
2 Which operator is used to access members of a structure variable?
3 Given struct Student *ptr;, how do you access the 'name' member?
4 What is the purpose of typedef with structures?
5 How do you declare an array of 10 Student structures?
6 What happens when you pass a structure to a function by value in C?
Answer all questions to check your score