Structs in C
Tags: computerscience, programming, c
Published: 2021-05-10 01:52:33

In this article, I will show you what structs are and how to effectively use them in your programs. I will go over the following:

<ol>
    <li>What are Structs</li>
    <li>Syntax of a Struct</li>
    <li>Declaring a Struct</li>
    <li>How to Initialize Struct Members</li>
    <li>Nested Structs</li>
    <li> Struct Pointers</li>
    <li> Pointer Data Members inside a Struct
    <li> Passing Structs into Functions</li>
</ol>

### 1) What are Structs?

Structs, or commonly known as Structure, are user-defined data-types. They help us group elements together that share something in common. 

### 2) Syntax of a Struct

```
struct [structure tag] {

   member definition;
   member definition;
   ...
   member definition;
} [one or more structure variables];  

```
<hr/>

### 3) Declaring a Struct

*Example 1*: 
```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    struct date {
        int month;
        int day;
        int year;
    } my_birthday;

    return EXIT_SUCCESS;
}
```

*Example 2*:

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    struct date {
        int month;
        int day;
        int year;
    };
   
    struct date my_birthday;
   
    return EXIT_SUCCESS;
}
```
<hr/>

### 4) How to Initialize Struct Members

It is important to note that you can not initialize data members inside a struct. You have to do it outide. Structs only hold the variables or data members. They do not hold the value.

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    struct date {
        int month = 5; //ERROR - Not allowed
        int day = 23; // ERROR - Not allowed
        int year = 2021; // ERROR - Not allowed
    } today_date;
    
    return EXIT_SUCCESS;
}
```

Let's see an example of how to correctly initialize values with a struct: 

*Example 1*
```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    struct date {
        int month;
        int day;
        int year;
     };

     struct date my_birthday[1] = {
        [0] = {.month = 6, .day = 15, .year = 1997}
     }
 
     printf("My birthday is %d/%d/%d",
            my_birthday[0].month,
            my_birthday[0].day,
            my_birthday[0].year);

     return EXIT_SUCCESS;
}  
```

*Example 2*

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]){
    struct date {
        int month;
        int day;
        int year;
     };

     struct date my_birthday[3] = {
        [0] = { 6, 15, 1997},
        [1] = {3, 23, 1989},
        [2] = {4, 29, 1979}
     };
 
     for(size_t i = 0; i < 3; ++i}{
         printf("My birthday is %d/%d/%d",
                my_birthday[i].month,
                my_birthday[i].day,
                my_birthday[i].year);
     };

     return EXIT_SUCCESS;
}  
```
<hr/>

### 5) Nested Structs

You can also have structs within structs. This is very helpful! Let's say we want a struct called date and it handles just the day, month, and year. Then, let's say we want another struct called time and it handles seconds, minutes, and hours. 

What if we want a struct called event that takes in month, day, year, minutes, seconds and hours? 

We can do that with nested structs. 

*Example*

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    struct date {
        int month;
        int day;
        int year;
    };

    struct time {
        int seconds;
        int minutes; 
        int hours;
    };

    struct date_and_time {
        struct date s_date;
        struct time s_time;
    };

    struct date_and_time event[1] = {
        [0] = {.s_date.month = 6,
               .s_date.day = 15,
               .s_date.year = 1997,
               .s_time.hours = 15,
               .s_time.minutes = 45,
               .s_time.seconds = 59 }
    };

    for(size_t i = 0; i < 1; ++i) {
        printf("I was born on %d/%d/%d at %d: %d: %d ",
                event[i].s_date.month,
                event[i].s_date_day,
                event[i].s_date_year,
                event[i].s_time.hours,
                event[i].s_time.minutes,
                event[i].s_time.seconds);
    };

    return EXIT_SUCCESS;
}
```
<hr/>

### 6) Pointer Structs

You can also have a struct pointer. This is very helpful. C is a pass-by-value language and we want to be as efficient as possible. If we were to pass a struct through a function as an argument, we would be copying the entire struct. However, with a struct pointe, we just copy the memory address. It is a lot more efficient. 

NOTE: In order to access data members with a struct pointer, you use -> operator as seen in the example below. 

*Example*

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    struct date{
        int month;
        int day;
        int year;
    };

    struct date new_date;

    struct date * p_date = &new_date;

    p_date->month = 6;
    p_date->day  = 15;
    p_date->year = 1997;

    printf("My birthday is %d/%d/%d",
            p_date->month,
            p_date->day,
            p_date->year); 
    
    return EXIT_SUCCESS;
}

```

### 7) Structs with Pointer Data Members

Structs can also have pointer data members. However, it is important to note that if you do decide to do this, you must dynamically allocate memory. 

Since, we are dynamically allocating memory using malloc(), we must also call free() to free up the memory. This is prevent memory leaks. 

Also, because have a struct pointer pointing to a struct of pointer data members, we must free the children, then the parent. 

Otherwise, you would just free the data members. 

*Example 1*

```
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char * argv[]) {
    struct create_struct {
        char * first_name;
        char * last_name;
    };

    struct create_struct full_name;

    struct create_struct p_full_name = &full_name;

    ptr_full_name->first_name = (char*)malloc(sizeof(char) * 50);
    ptr_full_name->last_name = (char*)malloc(sizeof(char)*50);

    strcpy(ptr_full_name->first_name, "Tomislav");
    strcpy(ptr_full_name->last_name, "Kraljic");

    printf("My name is %s %s",
            ptr_full_name->first_name,
            ptr_full_name->last_name);

    free(ptr_full_name->first_name); 
    free(ptr_full_name->last_name);
    free(ptr_full_name);

    return EXIT_SUCCESS;
}

```
<hr/>

### 8) Passing Structs into Functions

We can also pass structs as argument into functions. This is where pointer to a struct comes in handy! We just want to copy the memory address and pass it. We do not want to copy the entire struct and all its data. 

*Example 1*

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct create_my_struct {
    char * first_name;
    char * last_name;
} my_name;

struct create_my_struct * ptr_full_name = &my_name;

void create_data(struct create_my_struct * ptr_full_name);

void free_data(struct create_my_struct * ptr_full_name);

void print_data(struct create_my_struct * ptr_full_name);

int main(int argc, char * argv[]) {
    create_data(ptr_full_name);
    
    print_data(ptr_full_name);

    free_data(ptr_full_name);

    return EXIT_SUCCESS;
}

void create_data(struct create_my_struct * ptr_full_name) {
    ptr_full_name->first_name = (char*)malloc(sizeof(char)*50);
    ptr_full_name->last_name = (char*)malloc(sizeof(char)*50);
    
    strcpy(ptr_full_name->first_name, "Tomislav");
    strcpy(ptr_full_name->last_name, "Kraljic");
};

void print_data(struct create_my_struct * ptr_full_name) {
    printf("My name is %s %s",
            ptr_full_name->first_name,
            ptr_full_name->last_name);
};

void free_data(struct create_my_struct * ptr_full_name) {
    free(ptr_full_name->first_name);
    free(ptr_full_name->last_name);
    free(ptr_full_name);
};

```

<hr/>