C Structs & Pointers

Working with structure pointers efficiently

🎯 What are Structure Pointers?

Structure pointers allow you to access and manipulate structure data efficiently using memory addresses, enabling dynamic memory allocation and function parameter passing by reference.


// Structure pointer example
struct Student *ptr;
ptr->name = "John";  // Arrow operator
                                    

Pointer Operations

📍

Declaration

Declare structure pointers

struct Point *ptr;
🔗

Assignment

Point to structure variables

ptr = &point
➡️

Arrow Operator

Access members via pointer

ptr->x = 10;
💾

Dynamic Memory

Allocate memory at runtime

ptr = malloc(sizeof(struct Point));

🔹 Basic Structure Pointer

Pointers to structures provide efficient ways to access and manipulate structure members, especially when passing structures to functions. You can declare a structure pointer like struct Person *ptr; and access members using the arrow operator ptr->name instead of the dot operator. This is equivalent to (*ptr).name but more concise and readable. Structure pointers are essential because they avoid copying entire structures when passing them to functions, which saves memory and improves performance for large structures. They're also necessary for creating dynamic data structures like linked lists, trees, and graphs where structures reference each other through pointer members, forming complex relationships between data elements.

#include <stdio.h>

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

int main() {
    struct Student s1 = {"Alice", 20, 88.5};
    struct Student *ptr;
    
    // Point to the structure
    ptr = &s1
    
    // Access using arrow operator
    printf("Name: %s\n", ptr->name);
    printf("Age: %d\n", ptr->age);
    printf("Grade: %.1f\n", ptr->grade);
    
    // Modify using pointer
    ptr->age = 21;
    ptr->grade = 90.0;
    
    printf("\nAfter modification:\n");
    printf("Age: %d\n", s1.age);
    printf("Grade: %.1f\n", s1.grade);
    
    return 0;
}

Output:

Name: Alice
Age: 20
Grade: 88.5

After modification:
Age: 21
Grade: 90.0

🔹 Dynamic Memory Allocation

Dynamic memory allocation allows you to create structures at runtime using malloc, providing flexibility for programs that need variable amounts of data. The malloc() function allocates heap memory and returns a pointer, like struct Student *s = malloc(sizeof(struct Student)); which creates a structure that persists until explicitly freed. This approach enables creating data structures whose size isn't known at compile time, such as growing arrays or linked lists that expand as needed. Always check if malloc returns NULL, indicating allocation failure. Remember to call free() when done to prevent memory leaks. Dynamic allocation is fundamental for building scalable applications that efficiently manage memory resources based on actual runtime requirements.

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

struct Book {
    char title[100];
    int pages;
    float price;
};

int main() {
    struct Book *bookPtr;
    
    // Allocate memory for one Book structure
    bookPtr = (struct Book*)malloc(sizeof(struct Book));
    
    if (bookPtr == NULL) {
        printf("Memory allocation failed!\n");
        return 1;
    }
    
    // Assign values
    strcpy(bookPtr->title, "C Programming Guide");
    bookPtr->pages = 450;
    bookPtr->price = 29.99;
    
    // Display values
    printf("Book Details:\n");
    printf("Title: %s\n", bookPtr->title);
    printf("Pages: %d\n", bookPtr->pages);
    printf("Price: $%.2f\n", bookPtr->price);
    
    // Free allocated memory
    free(bookPtr);
    bookPtr = NULL;
    
    return 0;
}

Output:

Book Details:
Title: C Programming Guide
Pages: 450
Price: $29.99

🔹 Array of Structure Pointers

Arrays of structure pointers enable efficient management of multiple dynamically allocated structures, providing flexibility and memory optimization. You declare them like struct Employee *employees[100]; and allocate each element individually with malloc. This approach is powerful for managing collections of structures where each might have varying sizes or where you need to easily reorder elements by swapping pointers instead of copying entire structures. It's commonly used in applications like databases, contact managers, or any system managing multiple similar objects. Arrays of pointers also enable polymorphic behavior in C by pointing to different structure types, and they make sorting and searching operations more efficient by manipulating pointers rather than moving large data blocks.

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

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

int main() {
    struct Employee *employees[3];
    
    // Allocate memory for each employee
    for(int i = 0; i < 3; i++) {
        employees[i] = (struct Employee*)malloc(sizeof(struct Employee));
    }
    
    // Initialize employees
    strcpy(employees[0]->name, "John Doe");
    employees[0]->id = 101;
    employees[0]->salary = 50000.0;
    
    strcpy(employees[1]->name, "Jane Smith");
    employees[1]->id = 102;
    employees[1]->salary = 55000.0;
    
    strcpy(employees[2]->name, "Bob Johnson");
    employees[2]->id = 103;
    employees[2]->salary = 48000.0;
    
    // Display all employees
    printf("Employee List:\n");
    for(int i = 0; i < 3; i++) {
        printf("%d. %s (ID: %d) - $%.2f\n", 
               i+1, employees[i]->name, 
               employees[i]->id, employees[i]->salary);
    }
    
    // Free memory
    for(int i = 0; i < 3; i++) {
        free(employees[i]);
    }
    
    return 0;
}

Output:

Employee List:
1. John Doe (ID: 101) - $50000.00
2. Jane Smith (ID: 102) - $55000.00
3. Bob Johnson (ID: 103) - $48000.00

🔹 Passing Structures to Functions

Passing structures to functions via pointers is a best practice that improves performance and enables functions to modify the original structure. When you pass a structure by value like void func(struct Data d), the entire structure is copied, which is slow and memory-intensive for large structures. Using pointers like void func(struct Data *d) passes only the memory address, which is much faster regardless of structure size. Inside the function, use the arrow operator d->member to access members. If you want to prevent modifications, use const struct Data *d to indicate the function won't change the structure. This technique is essential for writing efficient C programs that work with complex data structures.

#include <stdio.h>

struct Rectangle {
    int width;
    int height;
};

// Function to calculate area using pointer
int calculateArea(struct Rectangle *rect) {
    return rect->width * rect->height;
}

// Function to modify rectangle using pointer
void doubleSize(struct Rectangle *rect) {
    rect->width *= 2;
    rect->height *= 2;
}

int main() {
    struct Rectangle r = {5, 3};
    
    printf("Original: %d x %d\n", r.width, r.height);
    printf("Area: %d\n", calculateArea(&r));
    
    doubleSize(&r);
    
    printf("After doubling: %d x %d\n", r.width, r.height);
    printf("New Area: %d\n", calculateArea(&r));
    
    return 0;
}

Output:

Original: 5 x 3
Area: 15
After doubling: 10 x 6
New Area: 60

🧠 Test Your Knowledge

What operator is used to access structure members through a pointer?