Structures in C
Group related data together! Create your own data types with struct. Store a student's name, age, and grade in one variable.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Define structures with struct keyword
- ✓Access members with the dot operator (.)
- ✓Use typedef for cleaner syntax
- ✓Create arrays of structures
01What is a Structure?
A structure (struct) is a user-defined data type that allows you to group different data types together under one name. Think of it as creating your own custom data type.
WHY Do We Need Structures?
The Problem: Without Structs
// Managing a student needs 4 separate variables!
char name1[50] = "Alice";
int id1 = 101;
float gpa1 = 3.85;
int age1 = 20;
// What about 100 students? 400 variables!
✓The Solution: With Structs
// Everything in one neat package!
struct Student alice = {
"Alice", 101, 3.85, 20
};
// 100 students? Just an array!
Think of It Like a Blueprint
struct Student
• name (text)
• id (number)
• gpa (decimal)
Blueprint (Template)
No memory yet!
student1
Alice
101
3.85
Actual Variable
student2
Bob
102
3.92
Actual Variable
🆚 Arrays vs Structures
Array
- • All elements same type
- • Access by index [0], [1], [2]...
- • Example: 5 integers
All same type (int)
Structure
- • Different types together
- • Access by name .age, .name
- • Example: Person data
Mixed types (string, int, float)
Real-World Examples
Student
name, ID, GPA
Book
title, author, price
Point
x, y coordinates
D Date
day, month, year
02Defining a Structure
1#include <stdio.h>23// Define a structure (blueprint)4struct Student {5 int id; // Member 1: integer6 char name[50]; // Member 2: string (char array)7 float gpa; // Member 3: float8 int age; // Member 4: integer9}; // Don't forget the semicolon!1011int main() {12 // Create a structure variable13 struct Student student1;14 15 // Assign values to members using dot (.) operator16 student1.id = 101;17 student1.gpa = 3.85;18 student1.age = 20;19 // For strings, use strcpy20 // strcpy(student1.name, "Alice");21 22 // Or initialize at declaration23 struct Student student2 = {102, "Bob", 3.92, 21};24 25 // Print values26 printf("Student ID: %d\n", student2.id);27 printf("Name: %s\n", student2.name);28 printf("GPA: %.2f\n", student2.gpa);29 printf("Age: %d\n", student2.age);30 31 return 0;32}Output:
Name: Bob
GPA: 3.92
Age: 21
How This Program Works
struct Student {...} — Defines a blueprint (template) for Student data. No memory allocated yet.
struct Student student1; — Creates an actual variable using the blueprint. Memory is now allocated.
student1.id = 101 — The . operator accesses member "id" and assigns value 101 to it.
{102, "Bob", 3.92, 21} — Initialize all members at once in order they were declared.
student2.name — Access the name member. Since it's a char array, use %s to print.
Key Points
- •
structkeyword defines the structure - • Members are listed inside curly braces {}
- • Semicolon
;required after closing brace - • Use
.(dot) operator to access members
03Structure Memory Layout
Structure members are stored in contiguous memory in the order they are declared. However, the compiler may add padding bytes for alignment.
Memory Layout: struct Point { int x; int y; }
4 bytes
4 bytes
Total: 8 bytes (contiguous memory)
Memory Padding & Alignment
The compiler adds padding bytes to align data on memory boundaries for faster access:
struct Example { char a; int b; char c; }
1B
3B
4 bytes
1B
3B
Total: 12 bytes (not 6!) due to padding
1#include <stdio.h>23struct WithPadding {4 char a; // 1 byte + 3 padding5 int b; // 4 bytes6 char c; // 1 byte + 3 padding7}; // Total: 12 bytes89struct Optimized {10 int b; // 4 bytes11 char a; // 1 byte12 char c; // 1 byte + 2 padding13}; // Total: 8 bytes (better!)1415int main() {16 printf("WithPadding size: %zu bytes\n", sizeof(struct WithPadding)); // 1217 printf("Optimized size: %zu bytes\n", sizeof(struct Optimized)); // 818 19 return 0;20}Optimization Tip
Order structure members from largest to smallest to minimize padding and save memory.
04typedef with Structures
typedef creates an alias (shorter name) for a type, so you don't have to write struct every time.
1#include <stdio.h>23// Without typedef - must use "struct Student" everywhere4struct Student {5 int id;6 char name[50];7};89// With typedef - can use just "Person"10typedef struct {11 int id;12 char name[50];13 int age;14} Person; // "Person" is now the type name1516int main() {17 // Without typedef18 struct Student s1 = {101, "Alice"};19 20 // With typedef (cleaner!)21 Person p1 = {201, "Bob", 25};22 Person p2 = {202, "Carol", 30};23 24 printf("Student: %s\n", s1.name);25 printf("Person 1: %s, Age: %d\n", p1.name, p1.age);26 printf("Person 2: %s, Age: %d\n", p2.name, p2.age);27 28 return 0;29}Without typedef
struct Student s1;struct Student s2;With typedef ✓
Person p1;Person p2;05Pointers to Structures & Arrow Operator
When you have a pointer to a structure, you use the -> (arrow) operator to access members instead of the dot.
When to Use . vs ->
. Dot Operator
// Direct variable
Student s;
s.age = 20;
Use with the struct itself
-> Arrow Operator
// Pointer to struct
Student *ptr;
ptr->age = 20;
Use with pointer to struct
What's Happening in Memory
ptr
0x1000
Pointer (stores address)
Student struct
Actual struct data @0x1000
ptr->age means: Go to address in ptr, then access age member
1#include <stdio.h>23typedef struct {4 char name[50];5 int age;6 float gpa;7} Student;89int main() {10 Student alice = {"Alice", 20, 3.85};11 12 // Create pointer to struct13 Student *ptr = &alice;14 15 // Access with DOT (using the variable directly)16 printf("Using dot: %s, %d\n", alice.name, alice.age);17 18 // Access with ARROW (using pointer)19 printf("Using arrow: %s, %d\n", ptr->name, ptr->age);20 21 // Modify through pointer22 ptr->age = 21;23 ptr->gpa = 3.90;24 25 printf("After modify: %s, %d, %.2f\n", 26 alice.name, alice.age, alice.gpa);27 28 return 0;29}Using dot: Alice, 20
Using arrow: Alice, 20
After modify: Alice, 21, 3.90
Arrow is Shorthand!
ptr->age is exactly the same as (*ptr).age. The arrow operator just makes it cleaner!
06Nested Structures
A structure can contain another structure as a member — this is called nesting.
1#include <stdio.h>23// Define a Date structure4typedef struct {5 int day;6 int month;7 int year;8} Date;910// Define a Person structure with nested Date11typedef struct {12 char name[50];13 int age;14 Date birthdate; // Nested structure!15} Person;1617int main() {18 // Initialize with nested values19 Person person1 = {20 "Alice",21 25,22 {15, 6, 1999} // birthdate: June 15, 199923 };24 25 // Access nested members with multiple dots26 printf("Name: %s\n", person1.name);27 printf("Age: %d\n", person1.age);28 printf("Birthdate: %d/%d/%d\n", 29 person1.birthdate.day,30 person1.birthdate.month,31 person1.birthdate.year);32 33 // Modify nested member34 person1.birthdate.year = 1998;35 36 return 0;37}Output:
Age: 25
Birthdate: 15/6/1999
07Array of Structures
You can create an array where each element is a structure — perfect for storing lists of records.
1#include <stdio.h>2#include <string.h>34typedef struct {5 int id;6 char name[30];7 float gpa;8} Student;910int main() {11 // Array of 3 students12 Student students[3] = {13 {101, "Alice", 3.85},14 {102, "Bob", 3.92},15 {103, "Carol", 3.78}16 };17 18 // Print all students19 printf("Student Records:\n");20 printf("%-5s %-15s %s\n", "ID", "Name", "GPA");21 printf("----------------------------\n");22 23 for (int i = 0; i < 3; i++) {24 printf("%-5d %-15s %.2f\n", 25 students[i].id, 26 students[i].name, 27 students[i].gpa);28 }29 30 // Find highest GPA31 float maxGpa = 0;32 int topStudent = 0;33 for (int i = 0; i < 3; i++) {34 if (students[i].gpa > maxGpa) {35 maxGpa = students[i].gpa;36 topStudent = i;37 }38 }39 printf("\nTop Student: %s with GPA %.2f\n", 40 students[topStudent].name, maxGpa);41 42 // Memory: 3 * sizeof(Student) bytes43 printf("\nSize of one Student: %zu bytes\n", sizeof(Student));44 printf("Size of array: %zu bytes\n", sizeof(students));45 46 return 0;47}Output:
ID Name GPA
----------------------------
101 Alice 3.85
102 Bob 3.92
103 Carol 3.78
Top Student: Bob with GPA 3.92
Size of one Student: 40 bytes
Size of array: 120 bytes
Array of Structures Memory Layout
students[0]
students[1]
students[2]
Each struct stored contiguously in memory
08Passing Structures to Functions
You can pass structures to functions by value (copy) or by pointer (reference). Each has different use cases.
By Value vs By Pointer
Pass by Value (Copy)
original
Alice, 20
copy
Alice, 20
✓ Original stays unchanged
✗ Copies entire struct (slow for large structs)
Use: When you don't need to modify
Pass by Pointer (Reference)
ptr
0x1000
@0x1000
Alice, 20
✓ Efficient (only copies address)
✓ Can modify original
Use: For large structs or when modifying
1#include <stdio.h>23typedef struct {4 char name[50];5 int age;6} Person;78// Pass by VALUE - receives a COPY9void birthday_value(Person p) {10 p.age++; // Only modifies the copy!11 printf("Inside function: %s is %d\n", p.name, p.age);12}1314// Pass by POINTER - receives the ADDRESS15void birthday_pointer(Person *p) {16 p->age++; // Modifies the ORIGINAL!17 printf("Inside function: %s is %d\n", p->name, p->age);18}1920int main() {21 Person alice = {"Alice", 25};22 23 printf("Before: %s is %d\n", alice.name, alice.age);24 25 birthday_value(alice); // Pass by value26 printf("After value: %s is %d\n", alice.name, alice.age); // Still 25!27 28 birthday_pointer(&alice); // Pass by pointer29 printf("After pointer: %s is %d\n", alice.name, alice.age); // Now 26!30 31 return 0;32}Before: Alice is 25
Inside function: Alice is 26
After value: Alice is 25 ← Unchanged!
Inside function: Alice is 26
After pointer: Alice is 26 ← Modified!
Best Practice
Use pointer when:
- • Struct is large
- • Need to modify the original
Use const pointer for read-only:
!Code Pitfalls: Common Mistakes & What to Watch For
These are the most common mistakes that trip up beginners. Study them carefully to avoid hours of debugging!
Forgetting Semicolon After struct
struct Point { int x; int y;} // ERROR! Missing semicolonstruct Point { int x; int y;}; // Correct!Assigning Strings with =
struct Student s;s.name = "Alice"; // ERROR! Can't assign strings like this// Correct way:strcpy(s.name, "Alice"); // Use strcpy()Comparing Structures with ==
struct Point p1 = {1, 2};struct Point p2 = {1, 2};if (p1 == p2) { // ERROR! Can't compare structs with ==}// Must compare member by member:if (p1.x == p2.x && p1.y == p2.y) { // Correct!}Watch Out When Copying Code!
Common Mistakes with Structures!
Structures seem simple but Code often makes subtle mistakes that lead to inefficiency or crashes:
1. Passing Large Structs by Value
Copied code often passes entire structs by value, copying all data — slow for large structures:
void process(struct BigData data) { // Copies entire struct! // Inefficient for large structs}// Better: Pass by pointervoid process(struct BigData *data) { data->field = value; // Fast, modifies original}2. Shallow Copy with Pointer Members
Copied code might copy a struct containing pointers — both structs then point to the same data:
struct Person p1 = {"Alice", 25};struct Person p2 = p1; // If name is char*, both point to same string!// Changing p2.name affects p1 too!3. Forgetting Struct Padding/Alignment
Copied code might assume sizeof(struct) equals sum of member sizes. Wrong! Compilers add padding for alignment. A struct with charand int may be 8 bytes, not 5.
Best Practices: Pass large structs by pointer, use const for read-only access, and always usesizeof() for struct sizes.
12Frequently Asked Questions
Q:When should I use a struct vs an array?
A: Use an array when you have multiple items of the same type (list of scores, collection of numbers). Use a struct when you need to group different types that belong together (a person with name, age, and salary). Arrays are for "many of the same," structs are for "one thing with multiple properties."
Q:What's the difference between . and -> operators?
A: Use . (dot) when you have the actual struct: person.name. Use-> (arrow) when you have a pointer to a struct:personPtr->name. Arrow is shorthand for(*personPtr).name. Simple rule: variable uses dot, pointer uses arrow.
Q:Why use typedef with structs?
A: typedef creates a shorter name so you don't have to write struct every time. Without: struct Person p; With:Person p; (after typedef struct {...} Person;). It's purely convenience — the struct works the same either way.
Q:Can I have a struct inside a struct?
A: Yes! This is called nesting. Example: A Date struct inside a Person struct for their birthday. Access with chained dots: person.birthday.year. You can nest as deep as needed, but keep it reasonable for readability.
Q:How do I copy a struct to another?
A: Simple assignment works:struct Person p2 = p1; copies all members.But beware: if the struct contains pointers, both copies point to the same data (shallow copy). For deep copies with pointer members, you must manually allocate and copy the pointed-to data.
Q:What is struct padding and why should I care?
A: CPUs access memory most efficiently at aligned addresses. The compiler adds padding bytes between struct members to ensure proper alignment. A struct with char then int may have 3 padding bytes after the char. This matters when: (1) writing binary files, (2) sending data over networks, (3) optimizing memory usage. Use sizeof() to check actual size.
Q:Should I pass structs by value or by pointer?
A: By pointer is usually better: faster (no copying), allows modification of the original. Use constto prevent modification: void print(const struct Person *p). Pass by value only for small structs (<16 bytes) that you want to remain unchanged, or when you specifically need a copy.
12Summary
✓What You Learned
- ✓Structures: Group different types together
- ✓Dot operator: Access members directly
- ✓Arrow operator: Access via pointer
- ✓typedef: Shorter type names
- ✓Nesting: Structs inside structs
- ✓Functions: Pass by value or pointer
Quick Reference
Define:
struct Name { type member; };
Create:
struct Name var = {...};
Access (direct):
var.member
Access (pointer):
ptr->member
Structure Usage Flow
struct {...}
Define
var = {...}
Create
var.member
Access
func(var)
Use
Test Your Knowledge
Related Tutorials
Unions in C
Unions are like structures but all members share the same memory. Useful for saving memory when you only need one field at a time.
Structures and Dynamic Memory
Combine structs with malloc! Create dynamic arrays of structures that grow as needed. Build real data structures.
Bit Fields in Structures
Pack data efficiently with bit fields! Learn to define structure members that use specific numbers of bits, saving memory in embedded systems and protocols.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!