Code of the Day
IntermediateStructs and enums

Defining and using structs

Group related data into custom types with C structs — declaration, member access, initialization, and passing structs to functions.

CIntermediate11 min read
By the end of this lesson you will be able to:
  • Declare a struct type and create variables of that type
  • Access struct members with the dot operator
  • Initialise a struct using designated initialisers
  • Pass a struct to a function by value and by pointer

A struct groups related variables of potentially different types into a single compound type. Structs are the primary way to model real-world entities in C — a point has x and y coordinates, a person has a name and age, a network packet has a header and a payload. Understanding structs is the gateway to every data structure you will build.

Declaring a struct

struct Point {
    double x;
    double y;
};

This declares a type called struct Point. The variables inside are called members (or fields). The declaration does not create any variable or allocate any memory — it only defines the layout of the type.

Creating a variable:

struct Point origin; /* a variable of type struct Point */
origin.x = 0.0;
origin.y = 0.0;

Member access with the dot operator

Use . to access struct members:

#include <stdio.h>

struct Rectangle {
    double width;
    double height;
};

double area(struct Rectangle r) {
    return r.width * r.height;
}

int main(void) {
    struct Rectangle rect;
    rect.width = 5.0;
    rect.height = 3.0;
    printf("Area: %.1f\n", area(rect)); /* 15.0 */
    return 0;
}

Initialisation

Aggregate initialisation (C89 compatible):

struct Point p = {3.0, 4.0}; /* x=3.0, y=4.0 in order */

Designated initialisers (C99, preferred):

struct Point p = { .x = 3.0, .y = 4.0 }; /* order-independent, clear */

Designated initialisers make the code self-documenting. Unmentioned members are zero-initialised.

Compound literal (C99):

struct Point midpoint(struct Point a, struct Point b) {
    return (struct Point){ .x = (a.x + b.x) / 2.0,
                           .y = (a.y + b.y) / 2.0 };
}

A compound literal creates a temporary struct value inline. Useful for returning structs from functions.

Passing structs to functions

By value: the function receives a complete copy. Modifying it does not affect the original:

void scale(struct Rectangle r, double factor) {
    r.width *= factor;  /* modifies the copy */
    r.height *= factor; /* original rect unchanged */
}

By pointer: the function receives an address and can modify the original:

void scale_inplace(struct Rectangle *r, double factor) {
    r->width *= factor;  /* -> is . combined with * */
    r->height *= factor;
}

int main(void) {
    struct Rectangle rect = { .width = 5.0, .height = 3.0 };
    scale_inplace(&rect, 2.0);
    printf("%.1f x %.1f\n", rect.width, rect.height); /* 10.0 x 6.0 */
    return 0;
}

r->width is shorthand for (*r).width. Use -> when accessing members through a pointer.

Struct size and padding

The compiler may insert padding bytes between struct members to satisfy alignment requirements:

struct Padded {
    char  c;  /* 1 byte */
              /* 3 bytes padding to align next int */
    int   i;  /* 4 bytes */
    char  d;  /* 1 byte */
              /* 3 bytes padding to keep struct size a multiple of 4 */
};
/* sizeof(struct Padded) is likely 12, not 6 */

Use sizeof(struct Padded) to check the actual size. Never assume structs are packed tightly. The Advanced tier covers alignment and packing in detail.

Copying structs

Assignment copies the entire struct:

struct Point a = {1.0, 2.0};
struct Point b = a; /* b is a complete copy of a */
b.x = 99.0;         /* does not affect a */

This is a shallow copy — if a struct contains a pointer, only the pointer is copied, not the pointed-to data. Both copies then point to the same underlying data.

A complete example: 2D distance

#include <stdio.h>
#include <math.h>

struct Point { double x; double y; };

double distance(struct Point a, struct Point b) {
    double dx = b.x - a.x;
    double dy = b.y - a.y;
    return sqrt(dx * dx + dy * dy);
}

int main(void) {
    struct Point p1 = {0.0, 0.0};
    struct Point p2 = {3.0, 4.0};
    printf("Distance: %.1f\n", distance(p1, p2)); /* 5.0 */
    return 0;
}

Compile with -lm to link the math library: gcc -Wall point.c -o point -lm.

Where to go next

Next: nested structs and arrays of structs — composing more complex types from simpler ones.

Finished reading? Mark it complete to track your progress.

On this page