Structures (struct) in C: Group Related Data Together 📦
Structures (struct) in C: Group Related Data Together is a core C concept covering master structures (struct) in C. Learn definition, declaration, member access with the dot operator, arrays of structs, pointers to structs, arrow operator, nested structs, and passing to functions. This topic is essential for academic learning, board exam preparation, and developing optimized real-world code.
Mentor's Note: Think of a
structas an index card or a student file folder. Instead of having separate sheets for names, roll numbers, and grades floating around, a folder binds them into a single physical unit. If you move the folder, all the data moves with it. Master structs, and you unlock the door to creating databases and complex data structures! 💡
📚 Educational Content: Structures are highly tested in GSEB Std 12 Computer Science, CBSE Class 12, and BCA Sem 1 examinations. You will frequently face theory questions about the differences between arrays and structs, code-writing tasks involving structure definitions, and output tracing questions involving structure pointers and member access.
:::info What You'll Learn By the end of this tutorial, you'll know:
- Why structures exist and how they solve the limitations of parallel arrays.
- How to define a structure template and declare structure variables.
- How to access and modify structure members using the dot (
.) operator. - How to manage lists of data using arrays of structures.
- How to work with structure pointers and the arrow (
->) operator. - How to nest structures inside other structures.
- How to pass structures to functions (by value vs by reference). :::
🌟 The Scenario: The Student Record Card
Imagine a school clerk who needs to manage information for 100 students. For each student, they must track:
- Roll Number (an integer)
- Name (a string/character array)
- Percentage Marks (a float)
If the clerk uses three separate list sheets (parallel arrays):
-
int roll_numbers[100]; -
char names[100][50]; -
float percentages[100]; -
The Logic: What happens if the clerk sorts the
percentagessheet to find the top student, but forgets to sort thenamesandroll_numberssheets? The grades get assigned to the wrong names! ❌ -
The Result: Student Roll #1 might end up with Student Roll #5's name and Student Roll #3's grade. The data becomes unsynchronized and corrupted. ✅
To solve this, we create a single index card for each student. The card groups the roll number, name, and percentage together. In C, this index card is called a structure (struct).
📖 Concept Explanation
In C, primitive types (like int, float, char) store single values. Arrays store multiple values, but they must all be of the same type (homogeneous). A structure is a user-defined data type that allows you to group variables of different data types (heterogeneous) under a single name.
1. Defining and Declaring a Struct
Defining a structure creates a custom blueprint. It tells the compiler what variables will live inside the structure.
struct Student {
int roll_no;
char name[50];
float percentage;
};
So what? The blueprint definition itself does not allocate memory in RAM. Memory is only allocated when you declare a structure variable:
struct Student s1; // Allocates memory for s1
2. Accessing Members using the Dot (.) Operator
To access or modify the internal variables (members) of a structure, we use the member access operator, which is the dot (.) operator.
s1.roll_no = 101;
s1.percentage = 85.5f;
So what? This syntax tells the compiler to look at the base address of s1 and offset it to read or write to the specific member variable.
3. Array of Structures
To store records for multiple students, we declare an array of structures.
struct Student class_ten[3]; // An array of 3 Student structures
So what? This creates a continuous block of memory large enough to hold three individual Student structures back-to-back, allowing us to loop through them like a database.
4. Pointers to Structures and the Arrow (->) Operator
When we have a pointer to a structure, accessing members using (*ptr).member is tedious. C provides the arrow (->) operator as a shortcut.
struct Student *ptr = &s1;
ptr->roll_no = 102; // Equivalent to (*ptr).roll_no = 102;
So what? The arrow operator dereferences the pointer and accesses the member in one clean, readable step.
5. Nested Structures
Structures can contain other structures as members. For instance, we can embed a Date structure inside a Student structure.
struct Date {
int day;
int month;
int year;
};
struct Student {
int roll_no;
char name[50];
struct Date dob; // Nested structure
};
So what? This allows you to model complex, hierarchical real-world data like adding birthdates, addresses, or marks sheets.
6. Passing Structures to Functions
We can pass structures to functions in two ways:
- By Value: A complete copy of the structure is passed. Changes inside the function do not affect the original. So what? If the structure is large, copying it wastes CPU cycles and RAM.
- By Reference (Pointer): The address of the structure is passed. Changes affect the original. So what? This is highly efficient because only a small pointer address (4 or 8 bytes) is passed.
🧠 Step-by-Step Logic
Let's design a program to manage student records:
- Start 🏁
- Define a nested
Datestructure and aStudentstructure. - Declare an array of
Studentrecords. - Input details (Roll No, Name, Percentage, DOB) for the students.
- Pass the array/pointers to a function that displays the data.
- Print the formatted student cards.
- End 🏁
💻 C Implementation (C99)
#include <stdio.h>
#include <string.h>
// Nested Structure to represent Date of Birth
struct Date {
int day;
int month;
int year;
};
// Main Structure to represent Student Record
struct Student {
int roll_no;
char name[50];
float percentage;
struct Date dob; // Nested struct
};
// Function prototype using pointer (By Reference) for efficiency
void printStudentDetails(const struct Student *s);
int main() {
// 🛒 Scenario: School Student Database
// 🚀 Action: Initialize records and print them using structure pointers
// Declaring and initializing structure variables
struct Student s1 = {101, "Aarav Mehta", 92.5f, {15, 8, 2010}};
struct Student s2;
// Assigning values manually to s2
s2.roll_no = 102;
strcpy(s2.name, "Diya Patel");
s2.percentage = 88.0f;
s2.dob.day = 22;
s2.dob.month = 4;
s2.dob.year = 2010;
// Printing details
printf("--- Student Database Records ---\n");
printStudentDetails(&s1); // Pass address
printStudentDetails(&s2); // Pass address
return 0;
}
// Function definition that accepts a read-only pointer to struct
void printStudentDetails(const struct Student *s) {
// Using arrow (->) operator to access members from a pointer
printf("Roll No : %d\n", s->roll_no);
printf("Name : %s\n", s->name);
printf("Percentage : %.2f%%\n", s->percentage);
// Accessing nested struct member: combining -> and . operators
printf("DOB : %02d/%02d/%d\n", s->dob.day, s->dob.month, s->dob.year);
printf("--------------------------------\n");
}
// Output:
// --- Student Database Records ---
// Roll No : 101
// Name : Aarav Mehta
// Percentage : 92.50%
// DOB : 15/08/2010
// --------------------------------
// Roll No : 102
// Name : Diya Patel
// Percentage : 88.00%
// DOB : 22/04/2010
// --------------------------------
📊 Sample Dry Run
Let's trace how the program executes:
| Step | Variable | Member Accessed | Value | Description |
|---|---|---|---|---|
| 1 | s1 | roll_no | 101 | Initialization of s1 |
| 2 | s1 | dob.day | 15 | Nested struct initialized |
| 3 | s2 | roll_no | 102 | Manual assignment |
| 4 | s | s->name | "Aarav Mehta" | Accessed via pointer in print function |
| 5 | s | s->dob.year | 2010 | Nested struct accessed via pointer |
📉 Complexity Analysis
Time Complexity ⏱️
- Member Access: $O(1)$ - Finding a member's value is a simple constant-time offset calculation.
- Copying Struct (By Value): $O(N)$ where $N$ is the size of the struct in bytes, because the entire block of bytes must be copied onto the stack.
- Passing Pointer (By Reference): $O(1)$ - Only the address is copied.
Space Complexity 💾
- Auxiliary Space: $O(1)$ - Memory is pre-allocated on the stack.
- Memory Footprint: Sum of the sizes of its members (plus padding bytes for memory alignment).
🎨 Visual Logic & Diagrams
Structure Memory Layout (With Alignment Padding)
Memory is arranged in 4-byte or 8-byte slots (word boundaries). To speed up reads, compilers place variables at aligned addresses. This introduces "padding bytes".
struct Student {
char a; // 1 byte
// 3 bytes padding (to align int to a 4-byte boundary)
int b; // 4 bytes
char c; // 1 byte
// 3 bytes padding
};
Total memory consumed = 12 bytes (instead of 6 bytes)!
🎯 Practice Problems
Easy Level 🟢
- Write a program to define a structure
Employee(ID, Name, Salary). Declare a variable, take inputs from the user, and print them.
Medium Level 🟡
- Define a structure
Timecontaining hours, minutes, and seconds. Write a function that takes twoTimevariables, adds them, handles carry-over (e.g., 65 seconds becomes 1 minute and 5 seconds), and returns the resulting structure.
Hard Level 🔴
- Create an array of 5
Studentstructures. Write a program to sort them in descending order based on their percentage marks, and display the ranked list.
❓ Frequently Asked Questions
Q: Why do we need a semicolon after a struct definition's closing brace?
In C, you can define a structure and declare variables of it in the same statement:
struct Student { int roll; } s1, s2;
The semicolon marks the end of the declaration list. Even if you don't declare any variables immediately, C syntax requires the semicolon to terminate the statement.
Q: What is the difference between the dot (.) and the arrow (->) operator?
Use the dot (.) operator when accessing members directly from a structure variable (e.g., s1.roll_no). Use the arrow (->) operator when accessing members through a pointer to a structure variable (e.g., ptr->roll_no).
Q: Can we compare two structure variables directly using s1 == s2?
No! In C, structures cannot be compared using the relational operator ==. This is because structure alignment padding contains garbage bytes, making a binary block comparison unreliable. You must compare structures member-by-member.
📚 Best Practices & Common Mistakes
✅ Best Practices
- Pass by Pointer: Always pass structures to functions using pointers (preferably
const struct MyStruct *ptrif read-only) to avoid copying overhead. - Initialize Comfortably: Use designated initializers like
struct Student s1 = {.roll_no = 101, .percentage = 95.0};for clarity. - Align Members: Place larger members (like
double,pointers) first in your structure definition to reduce padding bytes.
❌ Common Mistakes ⚠️
- Forgetting the Semicolon: Omitting the
;afterstruct MyStruct { ... };results in compilation errors likeexpected ';' after struct definition. - String Copying Error: You cannot assign strings directly using the
=operator (e.g.,s1.name = "John";is compilation error). You must usestrcpy(s1.name, "John");orstrncpy. - Using Arrow on Variables: Attempting to write
s1->roll_nowhens1is a variable, not a pointer, causes syntax errors.
✅ Summary
In this tutorial, you've learned:
- ✅ Structures group heterogeneous data variables together.
- ✅ Defining a structure establishes a blueprint, while declaring variables allocates actual memory.
- ✅ The dot (
.) operator accesses members of variables, whereas the arrow (->) operator accesses members of pointers. - ✅ Nested structures represent hierarchical structures like birthdays or addresses.
- ✅ Passing pointers of structures to functions saves memory and execution speed.
💡 Interview Tips & Board Focus 👔
- Memory Alignment & Padding: Interviewers love to ask "What is the
sizeofthis struct?". Be ready to explain how compilers pad structs to align members with word boundaries. - Struct vs Array: Board exams often ask you to differentiate them. Remember: Arrays are homogeneous (same type), structures are heterogeneous (different types).
📚 Further Reading
Continue your learning path:
- ← Previous: Dynamic Memory Allocation — review heap allocation
- Next: Unions in C — Shared Memory — learn about shared memory space