Structs

Table of contents
  1. Structs
    1. Creating a struct variable
    2. Initialising a struct
    3. Designated initialisers
    4. Different types in one struct
    5. Changing members
    6. Copying a struct
    7. Assigning a complete new value
    8. Structs and functions
    9. Returning a struct from a function
    10. Arrays of structs
    11. Structs containing arrays
    12. Comparing structs
    13. The struct keyword

A struct groups several related variables into a single type.

For example, a two-dimensional point has an x coordinate and a y coordinate. We could store these as separate variables:

int point_x = 10;
int point_y = 20;

However, these values belong together. A struct lets us group them:

struct point {
    int x;
    int y;
};

This defines a new structure called struct point. It contains two members, x and y.

Creating a struct variable

After defining the structure, we can create variables of that type:

struct point player_position;

We access each member using the dot operator .:

player_position.x = 10;
player_position.y = 20;

We can then use the members like normal variables:

#include <stdio.h>

struct point {
    int x;
    int y;
};

int main(void)
{
    struct point player_position;

    player_position.x = 10;
    player_position.y = 20;

    printf(
        "Position: (%d, %d)\n",
        player_position.x,
        player_position.y
    );

    return 0;
}

This prints:

Position: (10, 20)

Initialising a struct

A struct can be initialised when it is created:

struct point origin = {0, 0};

The values are assigned in the same order as the members in the definition:

struct point {
    int x;
    int y;
};

Therefore:

struct point position = {10, 20};

sets x to 10 and y to 20.

This works, but it is easy to put the values in the wrong order, particularly when a struct contains lots of members.

Designated initialisers

A designated initialiser identifies each member by name:

struct point destination = {
    .x = 45,
    .y = 76
};

This is normally clearer because we can see which value belongs to each member.

The members do not have to appear in the same order as the structure definition:

struct point destination = {
    .y = 76,
    .x = 45
};

Both versions produce the same result.

You can also provide values for only some of the members:

struct point position = {
    .x = 10
};

Here, x is set to 10 and y is set to zero.

Different types in one struct

The members of a struct do not all need to have the same type:

#include <stdbool.h>

struct sensor_reading {
    int sensor_id;
    float value;
    bool valid;
};

We can create and initialise a sensor reading:

struct sensor_reading reading = {
    .sensor_id = 3,
    .value = 24.5f,
    .valid = true
};

Remember that <stdbool.h> is needed to use bool, true and false.

We can access each member using .:

printf("Sensor: %d\n", reading.sensor_id);
printf("Value: %.1f\n", reading.value);

if (reading.valid) {
    printf("The reading is valid\n");
}

Changing members

Individual members can be changed after the struct has been created:

struct point position = {
    .x = 10,
    .y = 20
};

position.x = 15;
position.y += 5;

The final position is:

(15, 25)

Copying a struct

Unlike arrays, structs can be copied using =:

struct point first = {
    .x = 10,
    .y = 20
};

struct point second = first;

This copies the value of every member from first into second.

The two variables are still separate. Changing one does not change the other:

second.x = 100;

printf("first.x = %d\n", first.x);    // 10
printf("second.x = %d\n", second.x);  // 100

Assigning a complete new value

We can assign values to individual members:

position.x = 12;
position.y = 15;

We can also replace the complete struct using a compound literal:

position = (struct point) {
    .x = 12,
    .y = 15
};

The (struct point) part tells C what type of value is being created.

This is useful when several members should be updated together.

Structs and functions

A struct can be passed to a function in the same way as other variables:

#include <stdio.h>

struct point {
    int x;
    int y;
};

void print_point(struct point position)
{
    printf("(%d, %d)\n", position.x, position.y);
}

int main(void)
{
    struct point player_position = {
        .x = 10,
        .y = 20
    };

    print_point(player_position);

    return 0;
}

In this example, the complete struct is passed to print_point().

The function receives a copy, so changing it inside the function would not change the original variable.

Returning a struct from a function

A function can also return a struct:

struct point make_point(int x, int y)
{
    struct point new_point = {
        .x = x,
        .y = y
    };

    return new_point;
}

We can use it like this:

struct point destination = make_point(45, 76);

This is useful when a function needs to calculate and return several related values.

A shorter version is:

struct point make_point(int x, int y)
{
    return (struct point) {
        .x = x,
        .y = y
    };
}

Both versions do the same thing.

Arrays of structs

We can create an array containing several structs:

struct point path[] = {
    { .x = 0,  .y = 0  },
    { .x = 10, .y = 5  },
    { .x = 20, .y = 15 }
};

We use an array index to choose a struct, followed by . to choose one of its members:

printf("%d\n", path[1].x);

This prints:

10

We can process the complete array using a for loop:

#include <stdio.h>

struct point {
    int x;
    int y;
};

int main(void)
{
    struct point path[] = {
        { .x = 0,  .y = 0  },
        { .x = 10, .y = 5  },
        { .x = 20, .y = 15 }
    };

    size_t length = sizeof(path) / sizeof(path[0]);

    for (size_t i = 0; i < length; i++) {
        printf(
            "path[%zu] = (%d, %d)\n",
            i,
            path[i].x,
            path[i].y
        );
    }

    return 0;
}

Structs containing arrays

A struct member can also be an array:

struct student {
    char name[30];
    int marks[4];
};

For example:

struct student student = {
    .name = "Ash Ketchum",
    .marks = {65, 72, 58, 81}
};

Individual values are accessed in the usual way:

printf("Name: %s\n", student.name);
printf("First mark: %d\n", student.marks[0]);

Comparing structs

C does not allow complete structs to be compared using ==:

struct point first = { .x = 10, .y = 20 };
struct point second = { .x = 10, .y = 20 };

if (first == second) {
    // This is not allowed
}

Instead, compare the relevant members:

if (first.x == second.x && first.y == second.y) {
    printf("The points are equal\n");
}

A function can make this easier:

#include <stdbool.h>

bool points_are_equal(struct point first, struct point second)
{
    return first.x == second.x && first.y == second.y;
}

The struct keyword

In C, the full type name includes the struct keyword:

struct point position;

Writing only:

point position;

does not work with the definition used on this page.

Later, we will see how typedef can create a shorter type name:

point_t position;

For now, use struct point.


This site uses Just the Docs, a documentation theme for Jekyll.