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