Understanding how to group related data efficiently is a fundamental milestone for any programmer learning C. This capability is essential for modeling real-world entities like students, employees, or inventory items. Unlike arrays, which store collections of the same data type, a structure in C allows you to combine variables of different types under a single name. In this guide, we will explore the syntax, declaration, and practical application of structures through detailed example programs, moving from basic implementation to advanced concepts like nested structures and pointers.
Easier said than done, but still worth knowing.
What is a Structure in C?
A structure is a user-defined data type that enables the storage of heterogeneous data. While an integer variable holds one number and an array holds a list of numbers, a structure holds a collection of attributes describing a single entity. Consider this: think of it as a blueprint for a record. Take this case: a Book structure might contain a title (string), author (string), pages (integer), and price (float).
The keyword struct is used to define this composite type. Once defined, you can create variables (instances) of that type, just like you would with int or float.
Basic Syntax and Declaration
Before diving into complete programs, let’s establish the standard syntax. There are two primary ways to declare structure variables: alongside the definition or separately Most people skip this — try not to..
1. Defining the Structure Template:
struct Student {
char name[50];
int rollNumber;
float marks;
};
Note the semicolon after the closing brace; it is mandatory.
2. Declaring Variables:
You can declare variables inside main() or globally:
struct Student s1, s2; // Declares two variables of type struct Student
3. Using typedef for Cleaner Code:
Experienced developers often use typedef to create an alias, removing the need to write struct repeatedly.
typedef struct {
char name[50];
int id;
float salary;
} Employee;
// Now you can simply write:
Employee emp1;
Example Program 1: Basic Input and Output
This first example demonstrates the fundamental workflow: defining a structure, declaring a variable, accepting user input, and displaying the stored data. We use the dot operator (.) to access individual members Worth keeping that in mind..
#include
#include
// Structure Definition
struct Student {
char name[50];
int roll;
float percentage;
};
int main() {
// Declare a structure variable
struct Student std1;
// Input Data
printf("Enter student name: ");
// fgets is safer than gets; it prevents buffer overflow
fgets(std1.name, sizeof(std1.name), stdin);
// Remove trailing newline character from fgets
std1.name[strcspn(std1.
printf("Enter roll number: ");
scanf("%d", &std1.roll);
printf("Enter percentage: ");
scanf("%f", &std1.percentage);
// Output Data
printf("\n--- Student Details ---\n");
printf("Name: %s\n", std1.name);
printf("Roll Number: %d\n", std1.roll);
printf("Percentage: %.2f%%\n", std1.
return 0;
}
Key Takeaways:
- Dot Operator: Access members using
variable.member(e.g.,std1.roll). - String Handling: Since C doesn't have a native string type, we use character arrays.
fgetsis preferred overscanf("%s")because it captures spaces in names. - Memory Layout: The members are stored in contiguous memory blocks, allocated in the order they are declared.
Example Program 2: Array of Structures
In real applications, you rarely manage a single record. You usually handle a database of records. An array of structures is the standard approach for this. This program manages data for three employees.
#include
#include
struct Employee {
int id;
char name[30];
char department[20];
float salary;
};
int main() {
// Array of 3 structures
struct Employee emp[3];
int i;
// Loop for Input
for (i = 0; i < 3; i++) {
printf("\nEnter details for Employee %d:\n", i + 1);
printf("ID: ");
scanf("%d", &emp[i].id);
// Clear input buffer before reading string
while(getchar() !Practically speaking, = '\n');
printf("Name: ");
fgets(emp[i]. name, 30, stdin);
emp[i].name[strcspn(emp[i].
Not the most exciting part, but easily the most useful.
printf("Department: ");
fgets(emp[i].department, 20, stdin);
emp[i].department[strcspn(emp[i].
printf("Salary: ");
scanf("%f", &emp[i].salary);
}
// Display Header
printf("\n%-5s %-20s %-15s %10s\n", "ID", "Name", "Department", "Salary");
printf("-----------------------------------------------------------\n");
// Loop for Output
for (i = 0; i < 3; i++) {
printf("%-5d %-20s %-15s %10.2f\n",
emp[i].id, emp[i].name, emp[i].department, emp[i].
return 0;
}
Why this matters:
- Scalability: Changing the array size (e.g.,
emp[100]) scales the program instantly. - Indexing: Accessing
emp[i].salaryallows iterative processing—calculating totals, finding max/min salaries, or sorting records.
Example Program 3: Structures and Functions
Passing structures to functions is a critical concept. In C, function arguments are passed by value by default. This means passing a large structure creates a copy, consuming stack memory and CPU cycles. The efficient standard practice is passing by reference (using pointers).
Method A: Passing by Value (Creates a Copy)
Use this only for small structures where you want to protect the original data.
#include
struct Point {
int x, y;
};
// Function accepts a COPY of the structure
void movePoint(struct Point p, int dx, int dy) {
p.x += dx;
p.y += dy;
printf("Inside Function: (%d, %d)\n", p.x, p.
int main() {
struct Point p1 = {10, 20};
printf("Before Call: (%d, %d)\n", p1.x, p1.Day to day, y);
movePoint(p1, 5, 5);
printf("After Call: (%d, %d)\n", p1. x, p1.
### Method B: Passing by Reference (Pointer to Structure) — **Recommended**
This modifies the original data and avoids copying overhead. We use the **arrow operator (`->`)** to access members via a pointer.
```c
#include
#include
struct Product {
int code;
char name[30];
float price;
};
// Function accepts ADDRESS of structure
void applyDiscount(struct Product *p, float discountPercent) {
// Arrow operator: pointer->member
p->price = p->price - (p->price *
```c
discountPercent / 100);
printf("Updated Price: %.2f\n", p->price);
}
int main() {
struct Product item = {101, "Widget", 50.2f\n", item.price);
applyDiscount(&item, 10.0); // Pass address of item
printf("Final Price: %.Think about it: 0};
printf("Original Price: %. 2f\n", item.
**Output:**
Original Price: 50.00 Updated Price: 45.00 Final Price: 45.00
### Key Takeaways:
- **Efficiency:** Passing by reference avoids copying large structures, reducing memory and CPU overhead.
- **Modification:** Changes made inside the function directly affect the original data, which is critical for operations like updating records or calculations.
- **Use Case:** Pass by value is only useful when you need an immutable snapshot of the structure (e.g., calculations that shouldn’t alter the source data).
### Advanced Consideration:
For complex structures, consider using **const pointers** to enforce read-only access:
```c
void printProduct(const struct Product *p) {
printf("Code: %d, Name: %s, Price: %.2f\n", p->code, p->name, p->price);
}
This prevents accidental modifications and communicates the function’s intent clearly.
Conclusion
Structures in C are powerful tools for organizing related data, but their true potential is unlocked when combined with functions. By understanding how to pass structures efficiently—either by value or by reference—you gain fine-grained control over data manipulation and memory usage. Whether scaling a program to handle thousands of records or optimizing performance-critical code, these principles form the backbone of dependable, maintainable C programs. Mastery of these concepts ensures your code remains efficient, readable, and adaptable to evolving requirements Simple, but easy to overlook..