π‘ Tip: Rule of thumb β use -> through a pointer and . on an object. That covers 90% of cases.
What is a struct?
So far we've declared int, double, and char variables one at a time. But real-world data usually comes as a bundle of related values β for example, a person's name, age, and height.
A struct lets you group variables of different types into a single new type.
Without a struct...
char name[20]; int age; double height;
Add another person and you'll need name1, name2, ... β the variable count explodes.
With a struct
struct Person { char name[20]; int age; double height; };
Everything is bundled into a single Person.
Defining and Using a struct
Using a struct takes two steps: (1) define the type (the blueprint), then (2) declare a variable (an instance).
// (1) Define the struct type (blueprint)
struct Student {
char name[20];
int score;
};
// (2) Declare variables and use them
struct Student s1;
strcpy(s1.name, "Tanaka"); // access with dot (.)
s1.score = 85;
// You can also initialize at declaration
struct Student s2 = {"Sato", 92};
Access members with the dot operator (.):s1.score means "the score field inside s1."
Struct Memory Layout
Struct members are laid out in memory consecutively, in declaration order.
Memory layout of struct Student s1;
name[20] takes 20 bytes and score takes 4 bytes, so one Student uses at least 24 bytes (padding may add more).
π§± Padding Visualization β member order affects size
For efficient memory access, int needs a 4-byte boundary and double needs an 8-byte boundary (alignment). The compiler inserts padding between members automatically. Just reordering members can shrink a struct.
π§ͺ Pick a pattern
Pick a pattern
// No pattern selected
Size breakdown
Actual data: - B
Padding: - B
sizeof total: - B
β» Typical values on x86_64 Linux with gcc. Results vary by platform.
Byte-level layout (1 cell = 1 byte)
π‘ Pro tip: To save memory (embedded systems, huge datasets), declare members from largest to smallest (double β int β short β char). For cache-locality goals, different strategies apply.
Arrays of Structs
An array of structs lets you manage many records that share the same shape. This is where structs really shine.
// Print every student's score
for(int i = 0; i < 3; i++){
printf("%s: %d points\n", class[i].name, class[i].score);
}
Combine the array index with the dot operator, as in class[i].score. It reads naturally as "the i-th student's score."
Passing a struct to a Function
Structs can be passed by value (copied), but copying is expensive for large structs, so passing a pointer is common.
// Pass by value (the struct is copied)
void printStudent(struct Student s){
printf("%s: %d points\n", s.name, s.score);
}
// Pass by pointer (refers to the original)
void addBonus(struct Student *sp, int bonus){
sp->score += bonus; // equivalent to (*sp).score
}
addBonus(&s1, 5); // adds 5 points to s1.score
The arrow operator (->) is shorthand for accessing members through a pointer. sp->score is the same as (*sp).score.
typedef β Giving a Type an Alias
typedef lets you give a type a clearer alias. It's especially handy with structs.
Basic usage
// An alias for unsigned inttypedefunsigned int uint;
uint score = 100; // same as unsigned int score = 100;
Structs and typedef (important)
// Without typedef β you need "struct" every timestructPoint { int x, y; };
structPoint p1 = {3, 7}; // "struct" required// With typedef β you can drop "struct"typedefstruct { int x, y; } Point;
Point p1 = {3, 7}; // cleaner
In practice, typedef'd structs are the norm. The code is shorter and easier to read.
Enum (enum) β A Group of Named Constants
enum lets you name and group related integer constants β a great way to avoid magic numbers.
enumColor { RED, GREEN, BLUE }; // RED=0, GREEN=1, BLUE=2enumColor c = GREEN;
printf("%d\n", c); // 1
Specifying values
enumMonth {
JAN = 1, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC
}; // FEB=2, MAR=3, ... DEC=12 (auto-incremented)
enum + switch
enumDirection { UP, DOWN, LEFT, RIGHT };
enumDirection dir = UP;
switch (dir) {
case UP: printf("Up\n"); break;
case DOWN: printf("Down\n"); break;
case LEFT: printf("Left\n"); break;
case RIGHT: printf("Right\n"); break;
}
vs #define: You could write #define RED 0 instead, but an enum is treated as a type, so the compiler can help with type checks.
Method
Type check
Scope
Debugger display
#define RED 0
None
Whole file
Number only
enum { RED }
Yes
Controllable
Shown by name
Try It Yourself β Structs
Store three students in an array of structs and calculate the average score.
struct.c
Output
Click Run...
π‘ More ideas to try
Make an array of structs to hold multiple students
Pass a struct pointer to a function and modify its members