Structures, enumerations and types (9)

You may already asked how to create an array with different data types. Like a tuple. This is not possible, but we can create structures of data !

Structures

struct <identifier> {
    <type> <identifier>; // attribute 1
    <type> <identifier>; // attribute 2
    ...
};

This is an example creating a structure object :

#include <assert.h>

struct s_user {
    char* name;
    int age;
};

int main(void) {
    struct s_user user1 = { "John", 34 };
    assert(user1.age == 34);

    return 0;
}

As you can see, we can access to the object's elements with the following syntax :

<object identifier>.<attribute identifier>

Type definition

typedef struct <struct identifier> <type identifier>;

We usually define a type for structures we create.

typedef struct s_user {
    char* name;
    int age;
} User;
struct s_user {
    char* name;
    int age;
};
typedef struct s_user User;

It's also possible to make an anonymous structure while using the first method :

typedef struct {
    char* name;
    int age;
} User;

Enumerations

Imagine that we want to store the user's job, we could store it into a string. But a better way to do it exists : enumerations.

typedef enum e_job {
    Builder,
    Dentist,
    Engineer,
    Fireman,
    Lawyer,
    Teacher,
    Waiter
} Job;

We can add a Job attribute to our User structure :

typedef struct s_user {
    char* name;
    int age;
    Job job;
} User;

In some language, an enumeration element can be retrieved by the Enum::Element syntax. But in C, you directly have an access to the elements, without post fixing with the enumeration identifier.

Using enumerations is a smarter way to define something than a string object, and it takes less memory space.

int main(void) {
    User user1 = { "John", 34, Builder };
    printf("sizeof(User user1) == %li\n", sizeof(user1));
    printf("sizeof(Job Builder) == %li\n", sizeof(Builder));
    printf("sizeof(char* \"builder\") == %li\n", sizeof("builder"));

    return 0;
}
sizeof(User user1) == 16
sizeof(Job Builder) == 4
sizeof(char* "builder") == 8

With enumerations, it's possible to set a value for an element in the declaration :

typedef enum {
    Working = 1,
    Failed = 0,
    Freezed = -1
} State;

Methods with structures

The C language does not embed classes, so there are no methods. But, we can recreate this principle thanks to the pointers

User new_user(char* name, int age, Job job) {
    User temp = { name, age, job };
    return temp;
}

void print_user(User user) {
    printf("User(%s, %i, %s)\n", user.name, user.age, job_to_string(user.job));
}

void change_user_job(User* user, Job job) {
    user->job = job;
}

void free_user(User* user) {
    user->name = (char*) NULL;
    user->age = -1;
    user->job = Null;
    user = (User*) NULL;
}

What does user-><attribute> means ? Why we sometimes use it instead of user.<attribute> ?

When we manipulate a pointer of a structure object, we cannot directly access to the attributes. We have to get the pointer's pointing value (as seen in this article) :

int n = 5;
int* ptr_n = &n;
assert(*ptr_n == 5); // pointed value retrieved

So, we are supposed to do that :

(*user).job

We have to embrace user because *user.job cannot be done because it would mean that we want to retrieve the pointing value of job.

But, it's a boring syntax, so the C language allows to use the -> symbol instead, to make things easier.

(*user).job = user->job

Exercises

  1. Write the "Job element converter to string" function using a switch statement
  2. Create an enumeration with the 7 days of the week, every element has to be assigned by its day week number. In addition to that, create the following function :
    int is_weekend(DayWeek day_week);
    

Solutions

  1. Write the "Job element converter to string" function using a switch statement

    char* job_to_string(Job job) {
       switch (job) {
           case Builder:
               return "Builder";
           case Dentist:
               return "Dentist";
           case Engineer:
               return "Engineer";
           case Fireman:
               return "Fireman";
           case Lawyer:
               return "Lawyer";
           case Teacher:
               return "Teacher";
           case Waiter:
               return "Waiter";
           case Null:
               return "(null)";
       }
    }
    
  2. Create an enumeration with the 7 days of the week, every element has to be assigned by its day of the week number. In addition of that, create the following function :

    int is_weekend(DayWeek day_week);
    
    #include <assert.h>
    
    typedef enum {
       Monday = 1,
       Tuesday = 2,
       Wednesday = 3,
       Thursday = 4,
       Friday = 5,
       Saturday = 6,
       Sunday = 7
    } DayWeek;
    
    int is_weekend(DayWeek day_week) {
       // A weekday, not a day of the weekend
       if (day_week != 6 && day_week != 7) {
           return 0;
       }
       return 1;
    }
    
    int main() {
       assert(Monday == 1);
       assert(Sunday == 7);
       assert(is_weekend(Wednesday) != 1);
       assert(is_weekend(Saturday) == 1);
    
       return 0;
    }