Defining Types with typedef in C

The definition of new data types in C language is a common practice that allows improving code readability and maintainability. Using the typedef construct, programmers can create aliases for existing data types, simplifying code writing and making it more intuitive. This technique is particularly useful in large-scale projects, where the clarity and consistency of data types can make a big difference.

In this lesson, we will explore how to use typedef to define new data types in C.

Type Definition

In previous lessons, we have seen that the C language, at least up to the C99 standard, did not provide a properly boolean type.

To overcome this problem, in the dedicated lesson, we used as a workaround the use of a macro to define a boolean type:

#define BOOL int
#define TRUE 1
#define FALSE 0

In this way, whenever we needed a boolean type, we could declare a variable like this:

BOOL flag = TRUE;

In effect, with this workaround we created a new type, called BOOL, which is actually an int.

Macros, as we will see, work by text substitution. In other words, every time the preprocessor finds the string BOOL, it replaces it with int. The compiler, however, does not know the BOOL type, and therefore cannot perform type checking on it.

There exists, in C language, a more elegant way to define new types: the typedef construct.

Let's try to use it to define a boolean type:

typedef int BOOL;

The first thing to note is that, compared to the macro, we have reversed the order of int and BOOL. That is, the name of the new type must be inserted at the end.

The advantage of using typedef is that with it we create a new type recognized by the compiler. Now BOOL will be added to the list of types managed by the compiler and can be used in the same way.

We can define variables of type BOOL:

BOOL flag = TRUE;

The compiler will treat BOOL as a synonym for int. Therefore, the variable flag will be nothing more than an int.

To summarize:

Definition

Type Definition with typedef

The typedef construct allows defining new data types in C language. The syntax is as follows:

typedef type new_type;

Where type is the data type from which you want to create the new type, and new_type is the name of the new type.

Advantages of Using typedef

At this point, a question arises: but if a type defined with typedef is nothing more than an alias for another type, what is the advantage of using typedef?

  1. The first answer is that typedef makes programs more readable, provided that the choice of names for the new types is done carefully.

    For example, suppose we want to define a type to represent an amount of money. We can do it this way:

    typedef float Euro;
    

    Then we can declare variables of type Euro:

    Euro article_price, checking_account_balance;
    

    In this way, the line of code definitely becomes more readable compared to:

    float article_price, checking_account_balance;
    
  2. The second motivation lies in the fact that we can modify the underlying data type more simply and safely.

    Returning to the example above, if at some point we realize that we want to change the Euro type from float to double, we can do it very simply by changing a single line of code:

    typedef double Euro;
    

    The declaration of variables article_price and checking_account_balance will not change, and the compiler will take care of making the necessary conversions.

    If we had not used typedef, we would have had to change the type of all variables declared as float, with the risk of forgetting some.

  3. Finally, the third advantage concerns the simplicity of code writing when dealing with data structures, enumerations and pointers. We will see later how typedef can make the code more readable in these cases.

typedef and Code Portability

A final consideration concerns code portability.

The C language was designed to be portable. This means that a program written in C should be able to be compiled on any system, regardless of hardware architecture or operating system.

However, there is no guarantee that standard data types have the same size on all systems. For example, the int type might be 16 bits on one system and 32 bits on another.

For example:

int i = 100000;

If int is 16 bits, the value of i will be truncated to 16 bits, and the result will be different compared to when int is 32 bits. That is, the above code works on a 32-bit machine, but not on a 16-bit one.

So, in these cases we can use typedef to define integer types and modify their size according to needs.

Therefore, returning to the example above, we can define an INTEGER type that is an int on a 32-bit machine and a long on a 16-bit one:

/* 32-bit machine */
typedef int INTEGER;

/* 16-bit machine */
typedef long INTEGER;

By doing this, the declaration:

INTEGER i = 100000;

Will work on both machines, because INTEGER will be an int on one machine and a long on the other.

This technique does not solve all our portability problems also because many depend on the way variables are used.

For example, the problem arises with the use of the scanf and printf functions, which depend on the type of data passed as an argument. In these cases, it is necessary to be careful about how you write the code to ensure portability. The two functions require a different format specifier depending on the type of data passed: %d for int and %ld for long.

The C standard library itself, however, uses typedef to ensure code portability. For example, the size_t type is defined as unsigned int on some machines and unsigned long on others.

In general, the use of typedef can help make code more portable, but it does not solve all portability problems.

In Summary

In this lesson we have seen how to define new data types in C language using the typedef construct:

  • We have seen how to define a boolean type with typedef and the differences compared to using macros.
  • We discussed the advantages of using typedef to make code more readable and to facilitate modifying data types.
  • We talked about code portability and how typedef can help make code more portable.

In general, the use of typedef is a common practice in C language to define new data types and make code more readable and maintainable.