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.
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;
}
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;
}
strcpy() from <string.h> to copy strings into
structure members.
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!
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;
}
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;
}
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;
}
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;
}
*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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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)
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
Test Your Knowledge
Put your understanding of C structures to the test with these quiz questions!