Custom Types and Data Structures


You can access the slides 🖼️ for this lecture. All the code samples given here can be found online, alongside instructions on how to bring up the proper environment to build and execute them here. You can download the entire set of slides and lecture notes in PDF from the home page.

Here we cover how to create and use custom data types and structures in C.

Custom Types: typedef

To create a custom alias for a type, use the typedef keyword as follows:

typedef long long unsigned int my_int;  // 'my_int' is now equivalent to 'long long unsigned int'
int main(int argc, char **argv) {
    my_int x = 12;
    printf("x is: %llu\n", x);
    return 0;
}

typedef, followed by the type to alias, here long long unsigned int, followed by the name of the alias, here my_int. Following that, one can use my_int to refer to long long unsigned int: it is much shorter.

Custom Data Structures: struct

Custom data structures are extremely useful in C. They aggregate several fields of various types. They are created and manipulated with the struct keyword. On this example we define a struct person:

#include <stdio.h>
#include <string.h> // needed for strcpy

// Definition of the struct:
struct person {
    char name[10];
    float size_in_meters;
    int weight_in_grams;
};

void print_person(struct person p) {
    /* Access fields with '.' */
    printf("%s has a size of %f meters and "
        "weights %d grams\n", p.name,
        p.size_in_meters, p.weight_in_grams);
}

int main(int argc, char **argv) {
    // declare a variable of the struct type:
    struct person p1;
    // sets the variable field
    p1.size_in_meters = 1.6;
    p1.weight_in_grams = 60000;
    strcpy(p1.name, "Julie");
    struct person p2 = {"George", 1.8, 70000};
    print_person(p1);
    print_person(p2);
    return 0;
}

It has a name, which is a character array of length 10. A size_in_meter which is a float. And a weight in grams which is an int. In the main function we declare a struct person variable p1.

We can set values in the fields with the . operator. We do not detail strcpy here, just know that it sets Julie in the name field of p1. We can also do static initialisation with braces followed by the fields value in order. We have a print_person function that takes a struct person as parameter and prints its field values.

In memory, the fields of a struct are placed contiguously:

For our example, on x86-64 a char is one byte, so the array is 10 bytes in total. Followed by a float which is 4 bytes. Followed by an int which is also 4 bytes. So one instance of our struct should be 18 bytes. Note that the compiler may insert some padding for performance reasons.

Custom Data Structures and typedef

To avoid using the struct keyword each time one refer to a structure, typedef can be used:

struct s_person {
    /* fields declaration ... */
};
typedef struct s_person person;
void print_person(person p) { /* ... */}
int main(int argc, char **argv) {
    person p1;
    person p2 = {"George", 1.8, 70000};
    /* ... */
}

After the struct declaration, use typedef followed by the name of the struct, here s_person, followed by the name of the alias, here person. And now one can just use person. A faster method to do so is to typedef during the struct declaration:

typedef struct {
    /* fields declaration ... */
} person;

Enumerations

Enumerations are textual names that are mapped by the compiler to integer constants under the hood. They are useful in situation where integers are required, for example a switch, but when textual names are more meaningful. Here is an example, we define a color enum with 3 values: RED, BLUE, and GREEN:

enum color {
    RED,
    BLUE,
    GREEN
};

int main(int argc, char **argv) {
    enum color c1 = BLUE;
    switch(c1) {
        case RED:
            printf("c1 is red\n"); break;
        case BLUE:
            printf("c1 is blue\n"); break;
        case GREEN:
            printf("c1 is green\n"); break;
        default:
            printf("Unknown color\n");
    }
    return 0;
}

We declare an enum color variable c1 and sets it to BLUE. Then we can use it as the condition of a switch. Note that by convention constants are often in capital letters in C.

Same as for structs, enums can be used with typedef to avoid using the enum keyword So for example we simply use color to declare c2:

enum e_color {
    BLUE,
    /* ,,, */
};
enum e_color c1 = BLUE; // without typedef
typedef enum e_color color;
color c2 = RED;

Or, in a shorter way:

typedef enum {
    BLUE,
    /* ... */
} color;