Structures
A structure (struct) bundles several values of different types into one thing. A student has a name, an age and a grade average; a struct keeps them together.
Defining and using a struct
#include <stdio.h>
struct Student {
char name[20];
int age;
double gpa;
};
int main() {
struct Student s = {"Asha", 20, 3.8};
printf("%s is %d, GPA %.1f\n", s.name, s.age, s.gpa);
s.age = 21;
printf("Next year: %d\n", s.age);
return 0;
}Asha is 20, GPA 3.8
Next year: 21struct Student { ... }; describes the shape; struct Student s creates one. The dot reaches a member: s.age. The values in braces fill the members in order.
Naming the members, and typedef
#include <stdio.h>
typedef struct {
double x;
double y;
} Point;
int main() {
Point a = {1.5, 2.0};
Point b = {.y = 4.0, .x = 3.0}; // by name, in any order
Point origin = {0}; // every member zero
printf("a = (%.1f, %.1f)\n", a.x, a.y);
printf("b = (%.1f, %.1f)\n", b.x, b.y);
printf("origin = (%.1f, %.1f)\n", origin.x, origin.y);
return 0;
}a = (1.5, 2.0)
b = (3.0, 4.0)
origin = (0.0, 0.0)typedefgives the struct a short name, so you writePointinstead ofstruct Point.- Designated initialisers (
.x = 3.0) name each member, which reads better and survives reordering the struct. {0}sets everything to zero. Members you leave out of an initialiser are zero too.
Arrays of structs
#include <stdio.h>
typedef struct {
char name[16];
int score;
} Player;
int main() {
Player team[] = {{"Asha", 82}, {"Leo", 95}, {"Mei", 77}};
int n = sizeof team / sizeof team[0];
int best = 0;
for (int i = 0; i < n; i++) {
printf("%-5s %3d\n", team[i].name, team[i].score);
if (team[i].score > team[best].score) best = i;
}
printf("Top scorer: %s\n", team[best].name);
return 0;
}Asha 82
Leo 95
Mei 77
Top scorer: Leoteam[i].score reads naturally: element i of the array, then its score member. This is how C programs keep tables of records.
Passing a struct to a function
#include <stdio.h>
typedef struct {
char owner[16];
double balance;
} Account;
void deposit_copy(Account a, double amount) {
a.balance += amount; // changes the copy only
}
void deposit(Account *a, double amount) {
a->balance += amount; // a->balance means (*a).balance
}
int main() {
Account acc = {"Asha", 100.0};
deposit_copy(acc, 50);
printf("After deposit_copy: %.2f\n", acc.balance);
deposit(&acc, 50);
printf("After deposit: %.2f\n", acc.balance);
return 0;
}After deposit_copy: 100.00
After deposit: 150.00A struct passed by value is copied, so the function changes its own copy and the caller sees nothing. Pass a pointer to change the original, and reach the members with the arrow: a->balance is shorthand for (*a).balance.
Pointers are also cheaper for big structs, because only an address is copied. Use const Account *a when the function only reads.
Structs inside structs, and returning them
#include <stdio.h>
typedef struct { int day, month, year; } Date;
typedef struct { char title[24]; Date due; } Task;
Date next_day(Date d) { // simplified: every month has 30 days
d.day++;
if (d.day > 30) { d.day = 1; d.month++; }
if (d.month > 12) { d.month = 1; d.year++; }
return d;
}
int main() {
Task t = {"Submit report", {30, 12, 2026}};
printf("%s, due %02d/%02d/%d\n", t.title, t.due.day, t.due.month, t.due.year);
t.due = next_day(t.due);
printf("Moved to %02d/%02d/%d\n", t.due.day, t.due.month, t.due.year);
return 0;
}Submit report, due 30/12/2026
Moved to 01/01/2027A member can itself be a struct (t.due.day), and a function can return a whole struct. Assigning one struct to another with = copies every member, arrays inside it included.
You can't compare structs with ==, though. Compare the members you care about:
if (a == b) { } // error: invalid operands to binary ==
if (a.x == b.x && a.y == b.y) { } // compare member by memberSize and padding
#include <stdio.h>
struct A { char c; int i; char d; };
struct B { int i; char c; char d; };
int main() {
printf("sizeof(struct A) = %zu\n", sizeof(struct A));
printf("sizeof(struct B) = %zu\n", sizeof(struct B));
return 0;
}sizeof(struct A) = 12
sizeof(struct B) = 8Both hold the same three members (6 bytes of data), yet they differ in size. The compiler places each int at an address that's a multiple of 4, because processors read aligned values faster, and fills the gaps with padding. Ordering members from largest to smallest usually wastes the least space.