Structures
A struct groups related values of different types under one name — a book's title, author and year, or a point's x and y. Structures are how C models real-world things, and the building block of every larger data structure.
Defining and using a struct
#include <stdio.h> struct book { char title[64]; char author[48]; int year; double price; }; int main(void) { struct book b = { "The C Programming Language", "Kernighan & Ritchie", 1978, 49.99 }; printf("%s by %s (%d), %.2f\n", b.title, b.author, b.year, b.price); b.price = 39.99; // members are accessed with a dot b.year = 1988; printf("Second edition %d now costs %.2f\n", b.year, b.price); return 0; }
The C Programming Language by Kernighan & Ritchie (1978), 49.99 Second edition 1988 now costs 39.99
The definition creates a new type, struct book. Each variable of that type has its own copy of every member, reached with the . operator.
Initialising
#include <stdio.h> struct point { double x; double y; }; int main(void) { struct point a = { 1.5, -2.0 }; // in member order struct point b = { .y = 4.0, .x = 3.0 }; // designated initialisers (clearer) struct point origin = { 0 }; // everything zero printf("a(%.1f, %.1f) b(%.1f, %.1f) origin(%.1f, %.1f)\n", a.x, a.y, b.x, b.y, origin.x, origin.y); return 0; }
a(1.5, -2.0) b(3.0, 4.0) origin(0.0, 0.0)
Designated initialisers name each member, so they still work if the struct's members are reordered, and any member you leave out is zero.
Copying and assigning
Unlike arrays, structs can be assigned with = — every member is copied, including arrays inside the struct:
#include <stdio.h> #include <string.h> struct player { char name[16]; int score; }; int main(void) { struct player p1 = { "Ada", 120 }; struct player p2 = p1; // full copy strcpy(p2.name, "Alan"); p2.score = 95; printf("%s %d | %s %d\n", p1.name, p1.score, p2.name, p2.score); return 0; }
Ada 120 | Alan 95
Structs can't be compared with ==, though — compare the members you care about.
Structs and functions
Structs can be passed to and returned from functions. They're passed by value — the function gets a copy:
#include <stdio.h> #include <math.h> struct point { double x, y; }; double distance(struct point a, struct point b) { return hypot(b.x - a.x, b.y - a.y); } struct point midpoint(struct point a, struct point b) { return (struct point) { (a.x + b.x) / 2, (a.y + b.y) / 2 }; } int main(void) { struct point home = { 0, 0 }, shop = { 3, 4 }; struct point mid = midpoint(home, shop); printf("distance %.1f, midpoint (%.1f, %.1f)\n", distance(home, shop), mid.x, mid.y); return 0; }
distance 5.0, midpoint (1.5, 2.0)
(struct point) { … } is a compound literal: a temporary struct value written in place. For large structs, or to modify the caller's struct, pass a pointer instead (next lesson).
Arrays of structs
#include <stdio.h> struct product { const char *name; int stock; double price; }; int main(void) { struct product inventory[] = { { "Notebook", 40, 3.50 }, { "Pen", 150, 1.20 }, { "Backpack", 6, 39.90 }, }; size_t n = sizeof inventory / sizeof inventory[0]; double value = 0; for (size_t i = 0; i < n; i++) { printf("%-10s %4d x %6.2f\n", inventory[i].name, inventory[i].stock, inventory[i].price); value += inventory[i].stock * inventory[i].price; } printf("Stock value: %.2f\n", value); return 0; }
Notebook 40 x 3.50 Pen 150 x 1.20 Backpack 6 x 39.90 Stock value: 559.40
Nested structs
#include <stdio.h> struct date { int year, month, day; }; struct employee { char name[32]; struct date hired; double salary; }; int main(void) { struct employee e = { "Grace", { 2019, 3, 14 }, 52000 }; printf("%s was hired on %04d-%02d-%02d\n", e.name, e.hired.year, e.hired.month, e.hired.day); return 0; }
Grace was hired on 2019-03-14
Size and padding
A struct may be larger than the sum of its members: the compiler inserts invisible padding so that each member sits at an address suitable for its type. Ordering members from largest to smallest often reduces it:
#include <stdio.h> struct loose { char a; double b; char c; }; struct tight { double b; char a; char c; }; int main(void) { printf("loose: %zu bytes, tight: %zu bytes\n", sizeof(struct loose), sizeof(struct tight)); return 0; }
loose: 24 bytes, tight: 16 bytes
That's also why you should never assume a struct's size — always use sizeof.
Try it yourself
Store several students with their scores, then find and print the top student.
Top student: Liam with 94