Structures in C: Grouping Related Data
Structures in C let you group related variables of different types under one name. Instead of juggling separate variables for a student's name, roll number, and marks, you bundle them into a single struct. This makes your code organized, readable, and easy to pass around as one unit.
What is a structure in C?
A structure is a user-defined data type that holds several variables, called members, together. Each member can have its own type. Think of a structure as a form: one form for a student contains slots for name, roll, and marks. You fill many forms using the same layout.
Arrays force every element to share one type. A structure does not. That single difference is why structures matter for real programs like a college result system, a contact list, or an inventory record. When you learn C in Bangladesh, structures are usually the first step toward building anything practical.
Declaring a structure
You declare a structure with the struct keyword, a tag name, and a block of members. Declaration alone does not allocate memory; it only defines the blueprint. You create actual variables from that blueprint afterwards.
#include <stdio.h>
struct Student {
char name[50];
int roll;
float marks;
};
int main(void) {
struct Student s1; // create a variable of type struct Student
return 0;
}
Here struct Student is the type and s1 is the variable. You can declare many variables from the same structure: struct Student s1, s2, s3;. Each one gets its own copy of every member.
Accessing structure members
Use the dot operator (.) to read or write a member of a structure variable. The pattern is variable.member. You can assign values one by one, or initialize the whole structure when you declare it.
#include <stdio.h>
#include <string.h>
struct Student {
char name[50];
int roll;
float marks;
};
int main(void) {
struct Student s1;
strcpy(s1.name, "Rahim"); // copy text into a char array
s1.roll = 101;
s1.marks = 87.5f;
printf("Name: %s\n", s1.name);
printf("Roll: %d\n", s1.roll);
printf("Marks: %.2f\n", s1.marks);
return 0;
}
Note that s1.name is a character array, so you cannot write s1.name = "Rahim";. Use strcpy from <string.h> instead. Numeric members like roll and marks accept normal assignment.
Initializing at declaration
You can fill all members at once using braces. The values must appear in the same order as the members.
struct Student s2 = {"Karim", 102, 91.0f};
printf("%s scored %.1f\n", s2.name, s2.marks);
Array of structures
A single structure holds one record. To store many records, like a whole class, use an array of structures. Each array element is a complete structure with all its members.
#include <stdio.h>
struct Student {
char name[50];
int roll;
float marks;
};
int main(void) {
struct Student class[3] = {
{"Rahim", 101, 87.5f},
{"Karim", 102, 91.0f},
{"Sumi", 103, 78.0f}
};
for (int i = 0; i < 3; i++) {
printf("%s (Roll %d): %.1f\n",
class[i].name, class[i].roll, class[i].marks);
}
return 0;
}
You access a member of one element with class[i].marks. The array index picks the record; the dot operator picks the field inside it. This pattern scales to hundreds of students by changing only the array size and the loop limit.
Nested structures
A structure member can itself be another structure. This is called nesting, and it helps model real data. A student has a date of birth, and a date has its own day, month, and year. Group the date once and reuse it.
#include <stdio.h>
struct Date {
int day;
int month;
int year;
};
struct Student {
char name[50];
int roll;
struct Date dob; // a structure inside a structure
};
int main(void) {
struct Student s1 = {"Nadia", 104, {15, 8, 2003}};
printf("%s was born on %d/%d/%d\n",
s1.name, s1.dob.day, s1.dob.month, s1.dob.year);
return 0;
}
To reach a nested member, chain the dot operator: s1.dob.year. Read it left to right as "the year, inside the dob, inside s1". Nesting keeps related fields together instead of scattering dob_day, dob_month, and dob_year across the outer structure.
Structures and pointers
When you have a pointer to a structure, the dot operator no longer fits directly. Instead, C gives you the arrow operator (->) to access members through a pointer. ptr->member is shorthand for (*ptr).member.
#include <stdio.h>
struct Student {
char name[50];
int roll;
float marks;
};
int main(void) {
struct Student s1 = {"Rahim", 101, 87.5f};
struct Student *p = &s1; // pointer to the structure
printf("Name: %s\n", p->name);
p->marks = 90.0f; // update through the pointer
printf("Updated marks: %.1f\n", s1.marks);
return 0;
}
Pointers to structures matter because passing a whole structure to a function copies every byte. Passing a pointer copies only an address, which is faster and lets the function modify the original. This is why functions in larger C programs usually take a structure pointer.
Passing a structure to a function by pointer
void giveBonus(struct Student *s, float bonus) {
s->marks += bonus; // changes the caller's structure
}
Call it with giveBonus(&s1, 5.0f);. Because s points to s1, the update sticks after the function returns.
Using typedef to simplify structures
Writing struct Student everywhere gets repetitive. The typedef keyword creates a short alias so you can drop the struct word. This is the most common style in production C code.
#include <stdio.h>
typedef struct {
char name[50];
int roll;
float marks;
} Student; // Student is now a type name
int main(void) {
Student s1 = {"Karim", 102, 91.0f}; // no 'struct' needed
printf("%s: %.1f\n", s1.name, s1.marks);
return 0;
}
After the typedef, Student works exactly like a built-in type. You can write Student s1;, Student class[3];, or Student *p;. The structure's behavior does not change; only the name you type gets shorter and cleaner.
A complete worked example
This program ties the ideas together: an array of structures, a typedef alias, and a pointer passed to a function that finds the topper.
#include <stdio.h>
typedef struct {
char name[50];
int roll;
float marks;
} Student;
int topperIndex(Student arr[], int n) {
int top = 0;
for (int i = 1; i < n; i++) {
if (arr[i].marks > arr[top].marks) {
top = i;
}
}
return top;
}
int main(void) {
Student class[3] = {
{"Rahim", 101, 87.5f},
{"Karim", 102, 91.0f},
{"Sumi", 103, 78.0f}
};
int t = topperIndex(class, 3);
printf("Topper: %s with %.1f\n", class[t].name, class[t].marks);
return 0;
}
The array passes to topperIndex as a pointer automatically, so no copying of records happens. The function returns the index of the highest scorer, and main prints the result. This is a realistic shape for a small result-processing tool.
Frequently asked questions
What is the difference between the dot (.) and arrow (->) operators?
Use the dot operator when you have an actual structure variable, like s1.marks. Use the arrow operator when you have a pointer to a structure, like p->marks. The arrow is just a shortcut for (*p).marks, which dereferences the pointer and then accesses the member.
Why use typedef with structures?
The typedef keyword creates a shorter alias so you can write Student s1; instead of struct Student s1; every time. It reduces repetition and makes declarations read like built-in types. The structure itself behaves identically; only the name you type becomes cleaner and faster to write.
Can a structure contain another structure?
Yes. A member can be another structure, which is called nesting. For example, a Student can contain a Date for date of birth. You access nested members by chaining the dot operator, such as s1.dob.year. A structure cannot contain itself directly, but it can hold a pointer to its own type.
What happens when I pass a structure to a function?
By default, C passes structures by value, copying every member into the function. For large structures this wastes time and memory, and changes inside the function do not affect the original. Passing a pointer instead copies only an address, runs faster, and lets the function modify the caller's data.
Keep practicing
Structures are the foundation for linked lists, trees, and almost every data-heavy C program you will write next. Build small projects: a phone book, a marks ledger, a simple inventory. Each one reinforces declaration, member access, arrays, and pointers together. Strong C fundamentals open doors to software roles; explore openings at Avian™ Career when you are ready to apply your skills professionally.
Categories
- Tutorial
- Announcement
- News