Introduction to C —6 — Type definitions: typedef and Understanding C type declarations

What is a typedef ?

Aserdargun
4 min readOct 27, 2023

--

Types can sometimes be either complex or non-descriptive. For example, the size of a data structe may return an unsigned int on one system, while on another it return an unsigned long . It would be much more helpful to a programmer to have a standard name for the type returned by an operator such as sizeof or a function that performs a similar task. Thus, the following alias for the type returned by functions and operators related to memory used is

typedef unsigned int size_t;

Thus, any time a variable is declared to be unsigned int and it will behave as an unsigned int .

One issue in C and C++ is that there is no default size of int or long . Consequently, many libraries designed for a specific system will redefine types to indicate their size:

typedef signed char           S8;
typedef char U8;
typedef short S16;
typedef unsigned short U16;
typedef int S32;
typedef unsigned int U32;
typedef long long S64;
typedef unsigned long long U64;

By default, char is unsigned while short , int , and long are signed. Now if a variable is declared to be of type U32 , the programmer knows it is a 32-bit unsigned integer.

You can also define a type to be an array of a given type, such as

typedef U32 OS_SEM[2];
typedef U32 OS_MUT[3];
typedef U32 *OS_ID;

In this case, variables, arguments, and return types declared to be OS_SEM , OS_MUT , and OS_ID will be, respectively, an array of two 32-bit unsigned integers, an array of three 32-bit unsigned integers, and a pointer to a 32-bit unsigned integer.

Substitution rule

Whenever you see a variable, argument, or return value declared to be a type that has been defined through typedef , you need only substitute the variable for the typedef symbol in the typedef definition. For example, if

typedef struct{
S8 name[256];
U32 size;
U16 fileID;
U8 attrib;
RL_TIME time;
} FINFO;

and a variable is defined as an array of three instances of this type:

FINFI array[3];

this would be equivalent to

struct {
S8 name[256];
U32 size;
U16 fileID;
U8 attrib;
RL_TIME time;
} array[3];

Using typedef with struct

A struct may be given a name in two ways:

struct struct_name{
/* fields */
}

typedef struct {
/* fields */
} Struct_name;

These two can be combined:

struct struct_name {
/* fields */
} Struct_name;

In the first case, you must always use the keyword struct , but in this case, the symbol struct_name can be continue to be used as either a variable or a function name: prefixed by struct it means the structure, and without the prefix it refers to the function or variable. With the typedef , the defined alias is now global.

Note that the alias is defined at the end of the statement: consequently, using typedef , one cannot define a pointer to the structure within the structure. If you wanted to define a node within a linked list, and you wanted a typedef , you would have to try something like:

typedef struct struct_name{
int value;
struct struct_name *next; /* a pointer to this structure */
} Struct_name;

Understanding C type declarations

Why is * used for both declaring pointers and dereferencing them? Basically, the notation of a variable declaration says what you must do to something to get something to get something of the specified type. For example:

  • int x; To get an integer, just use x.
  • int *x; To get an integer, you must use *x.
  • int x[3]; To get an integer, you must use x[n].
  • int f(int, double); To get an integer, you must call f(n, x) where n is an integer, and x is a double.
  • int *g(void); To get an integer, you must call g() and then dereference the result.

This is always he way that variables are declared in C.

Now, how about the declaration

int *r[10];

Is this

  • A pointer to an array of integers, or
  • An array of 10 pointers to integers?
  • At this point, you must look at an operator precedence table to determine that if you were to call

int (*r)[10];

That is, if you dereference r and then use brackets, you get an integer.

For more fun with C declarations, https://cdecl.org/

int const *r versus int *const r

One of these declares that r cannot be changed, while the other declares that the value of r cannot be changed. From our reasoning above, we should be able to determine this:

int const *r The thing that is consist is *r , that is, the integer value is constant. Therefore, you can assign to r , but you cannot assign to *r

int *const r The thing that is const is r , that is, something that must be dereferenced before it can be interpreted as an integer. Therefore, you can assign to *r, but you cannot assign to r .

Note that const int *r is identical to int const *r

Also note that int const *const r and const int *const ralso both mean the same thing: you cannot assign to r and you cannot assign to *r , either.

Reference

--

--

Aserdargun

Innovator, Problem Solver, Python Geek, Data Scientist , Mechanical and Industrial Engineer, Lean Practitioner, Lean 6 Sigma B.Belt