Struct as Data Type in C

In the previous lesson we saw how to declare a struct variable in C language composed of multiple fields of heterogeneous type.

In this lesson we will see how to define a data type starting from a data structure in C language. In this way we will be able to declare multiple variables starting from a single data type.

The advantage is that the variables declared in this way will be compatible with each other and we will be able to assign the content of one data structure to another.

The Problem of Declaring a struct

In the previous lesson we saw how to declare struct variables. However, when declaring them, we had to always also define how the structure was composed.

Returning to the example from the previous lesson, we defined the "telephone contact" variables in this way:

struct {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
} contact1, contact2;

Now, suppose we need to declare many variables of this type. In the example we have declared two contact1 and contact2. However we might have the need to declare many.

As long as we declare them in the same place the problem does not arise, but if we need to declare them in different points of the program, we must declare the structure each time.

For example, in one point we should declare contact1:

struct {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
} contact1;

and, subsequently, in a second point we should declare contact2:

struct {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
} contact2;

In essence, we had to rewrite the entire definition of the fields of the two structures. It is obvious that in the long term we can run into very serious problems:

  • The size of the source code increases with repeated information;
  • In case we need to modify the structure, for example by adding fields or modifying the type of members, we must modify the code in all the points where it was declared. This is an error-prone procedure.

The most serious problem, however, is the fact that according to the C standard the variables contact1 and contact2 do not have the same type. The consequence is that we cannot assign the value of the variable contact1 to the variable contact2:

/* ERROR */
contact2 = contact1;

Not only that, but not having a name we cannot use the type of contact1 or contact2 as the parameter type of a function.

To overcome these problems we must find a way to define the type represented by a struct structure and not declare a struct variable. Having done this, subsequently, we can declare variables of that specific type.

The C language standard provides two mechanisms:

  • Tagged structs (structures or struct);
  • Define a type through typedef.

In this lesson we will see these two mechanisms in detail.

Tagged struct

The tag of a structure is, essentially, a name that serves to identify a particular type of structure.

The syntax for defining the tag of a structure is very simple: just interpose the tag between the keyword struct and the definition of the structure itself.

For example, wanting to associate with the struct structure that we defined in the previous lesson the tag telephone_contact, we must write:

struct telephone_contact {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
};

Note that in the definition we inserted a semicolon at the end of the structure definition. This is necessary to indicate to the compiler that the structure definition is finished.

Note

Attention to the semicolon at the end of a structure definition

Omitting the semicolon at the end of a data structure definition can lead to compilation errors that are difficult to interpret.

Let's take the following example:

struct example {
    float member1;
    int member2;
}                   /* ERROR: We forgot the semicolon */

function(void) {
    /* ... */
}

In this case the compiler recognizes, incorrectly, that the function function returns a value of type struct example. This is because the compiler did not find the semicolon at the end of the structure definition.

The problem is that often compilers only notice this when the point is reached where the function ends, returning cryptic errors.

Once we have assigned a tag to a struct we can use it to declare variables of that type.

For example, wanting to declare two variables of type telephone_contact we must write in this way:

struct telephone_contact contact1, contact2;

Keep in mind that the keyword struct must be inserted anyway. In fact, if we had written in this way the compiler would return an error:

/* ERROR */
telephone_contact contact1, contact2;

The reason is that telephone_contact does not identify a type; without the keyword struct the declaration is meaningless.

This has important consequences. Since the tag of a struct is meaningless to the compiler, it cannot conflict with other names used within our program.

For this reason, the following code is perfectly legal:

struct telephone_contact {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
};

struct telephone_contact contact1, contact2;

/* Legal declaration */
int telephone_contact;

In this case, telephone_contact is a variable of type int while contact1 and contact2 are variables of type struct telephone_contact. The code is legal even if not very readable.

Definition

Tagged Structures

A Tagged Structure is a data structure to which a name, or tag, has been associated that allows it to be identified.

The syntax for defining a tagged structure is:

struct structure_name {
    /* ... */
};

Once a struct has been tagged, variables of that type can be declared using the following syntax:

struct structure_name variable_name;

Combination of Tagged struct and Variable Declarations

We have seen how we can define a tagged struct and how we can declare variables of that type.

These two operations can be combined in a single step. For example, wanting to declare two variables of type telephone_contact we must write in this way:

struct telephone_contact {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
} contact1, contact2;

With this code we have declared both a tagged structure of type struct telephone_contact and two variables of that type: contact1 and contact2.

Compatibility Between Variables of Tagged struct Type

One of the main advantages in tagging a struct is that the variables of that type are compatible with each other.

For this reason the following code is perfectly legal:

struct telephone_contact contact1 = {
    "Mario",
    "Rossi",
    30,
    "1234567890",
    "mario.rossi@test.com"
};

struct telephone_contact contact2 = contact1;

The assignment of contact1 to contact2 is perfectly legal. This is because contact1 and contact2 are variables of the same type.

typedef and struct

As an alternative to tagged structures, the C language provides another mechanism for defining new data types: typedef.

We can, in fact, use typedef to define a new data type that corresponds to a struct.

Returning to the example of telephone_contact we can use typedef in this way:

typedef struct {
    char name[20];
    char surname[20];
    int age;
    char phone_number[20];
    char email_address[20];
} telephone_contact;

In this way we have defined a new data type telephone_contact that corresponds to a struct with the fields we have defined.

The thing to note is that the name of the new type must be placed at the end of the struct definition and not at the beginning as happens with tagged structures.

At this point we can use telephone_contact as if it were any type of the C language, so we can declare two variables, contact1 and contact2, in this way:

telephone_contact contact1, contact2;

Since telephone_contact is a full-fledged type, it is no longer necessary to use the keyword struct before the type name.

In general, in the C language, we can use both tagged structures and typedef to define new data types. There are no particular constraints in this regard. There are, however, special cases in which we must necessarily use tagged structures as we will see in the future.

Definition

typedef of a data structure

In C language it is possible to define a new data type starting from a struct using the keyword typedef.

The syntax for defining a new data type starting from a struct is:

typedef struct {
    /* ... */
} new_type_name;

Once the new data type is declared, we can use new_type_name as if it were any type of the C language. The syntax for declaring a variable is:

new_type_name variable_name;

In Summary

In this lesson we examined two ways to define a type starting from a struct or data structure:

  • Tagged structures;
  • Structure data types defined with typedef.

In this way we are able to declare variables belonging to the same type. The main advantage will be obvious in the next lesson in which we will study how to pass a data structure as an argument to a function and how to return it as a return value.