Notifications

No notifications

/Phase 3

Structures (struct & typedef)

A struct lets you bundle several variables of different types into ONE named unit. It's how C programs model "things" — a Student, a Point, a Date, a Book.

struct Student {
    int    id;
    char   name[50];
    float  cgpa;
};

struct Student s = {1, "Asha", 8.9f}; printf("%s scored %.2f\n", s.name, s.cgpa);

The Two Access Operators

You haveUse
A struct value (s)s.field (dot)
A struct pointer (p)p->field (arrow)

struct Student *p = &s;
p->cgpa = 9.1f;          /* same as (*p).cgpa */

typedef — Skip the "struct" Word

typedef struct {
    int x, y;
} Point;

Point a = {3, 4}; /* no need to write "struct Point" */

Why Structs Matter

They are the foundation of every data structure in C — linked lists, trees, graphs, hash tables — all built from structs that contain pointers to other structs.

On this page

Detailed Theory

Structs are how C programs talk about real-world entities. Without them you'd be juggling parallel arrays — int ids[100], char names[100][50], float cgpas[100] — which is a nightmare to keep in sync. With structs you have ONE Student[100] and every field travels together.

Defining a Struct

struct Point {
    int x;
    int y;
};

  • The block declares a type named struct Point.
  • No memory is allocated yet — it's just a blueprint.
To create a variable:

struct Point p1;            /* uninitialised */
struct Point p2 = {3, 4};   /* x=3, y=4 */
struct Point p3 = {.y = 7}; /* designated init: x=0, y=7 (C99) */

typedef — A Cleaner Name

Writing struct Point everywhere is annoying. typedef gives the type a one-word name:

typedef struct Point Point;        /* now "Point" means "struct Point" */
Point p = {1, 2};

The common one-shot form:

typedef struct {
    int x, y;
} Point;

This declares an anonymous struct and aliases it as Point. After that, you just write Point. Almost all real C code uses this style.

Accessing Fields: . vs ->

Point p = {3, 4};
p.x = 10;            /* dot — for a value */

Point *q = &p; q->y = 20; /* arrow — for a pointer */ (*q).y = 20; /* same thing, ugly */

The arrow -> is just sugar for "dereference then dot". Use it whenever you have a pointer.

Passing Structs to Functions

Two choices: by value (copy) or by pointer (reference).

/* By value — function gets its own copy */
void print_point(Point p) {
    printf("(%d, %d)\n", p.x, p.y);
}

/* By pointer — function can modify the original */ void translate(Point *p, int dx, int dy) { p->x += dx; p->y += dy; }

For anything bigger than a few ints, pass by pointer to avoid copying. Add const if you only read:

void print_student(const Student *s);   /* fast + safe */

Returning Structs

Yes, you can return a struct by value:

Point make_point(int x, int y) {
    Point p = {x, y};
    return p;
}

Point origin = make_point(0, 0);

Modern compilers optimise this (return-value optimisation), so don't fear it for small structs.

Nested Structs

typedef struct {
    int day, month, year;
} Date;

typedef struct { char title[100]; Date published; float price; } Book;

Book b = {"K&R C", {1, 2, 1978}, 499.0f}; printf("%d/%d\n", b.published.day, b.published.month);

Arrays of Structs

Student class[3] = {
    {1, "Asha", 8.9f},
    {2, "Ravi", 7.5f},
    {3, "Mira", 9.2f},
};

for (int i = 0; i < 3; i++) printf("%s — %.1f\n", class[i].name, class[i].cgpa);

Self-Referential Structs (the linked list)

A struct CAN contain a pointer to its own type — this is how linked structures work:

typedef struct Node {
    int          value;
    struct Node *next;       /* must say "struct Node" — typedef name not yet visible */
} Node;

A node knows the next node. Chain them up and you've got a linked list. Same trick gives you trees (two child pointers), graphs (an array of pointers), etc.

Padding & Alignment (Brief)

The compiler may insert invisible "padding" bytes between fields so each one sits on a CPU-friendly address. Means sizeof(struct) can be bigger than the sum of its fields.

struct S {
    char  c;     /* 1 byte */
    int   i;    /* 4 bytes — usually preceded by 3 bytes of padding */
};
/* sizeof(struct S) is often 8, not 5 */

You normally don't care. If you do (e.g., writing structs to disk or over a network), put the largest fields first and read about #pragma pack.

Struct Cheat-Sheet

ActionSyntax
Definestruct T { ... }; or typedef struct { ... } T;
DeclareT x;
InitT x = {1, 2};
Designated initT x = {.field = val};
Field via valuex.field
Field via pointerp->field
Sizesizeof(T) (may include padding)
Heap allocateT *p = malloc(sizeof *p);

Master structs and you can model anything — and you're one step away from linked lists, trees, and the data structures that make C truly powerful.