Why Embedded C Programming Prefers Typedef?

Click the above“Embedded and Linux Matters”, select“Pin/Star the Official Account”

Welfare and valuable content delivered promptly

Abstract: Different projects have different coding styles and “quirks”. After reading enough code, you will find that some code prefers macros while others prefer using typedef. So what are the benefits of using typedef? Why do many people like to use it?

1. Basic Usage of typedef

1.1 Combining typedef with Structures

typedef is a keyword in C language used to give a type an alias, essentially giving a new name to an existing type in C. While reading code, you will often see typedef used in conjunction with structures, unions, enumerations, and function pointer declarations. For example, the following is a declaration and usage of a structure type:

struct student
{
  char name[20];
  int  age;
  float score;
};
struct student stu = {"wit", 20, 99};

In C, to define a structure variable, we usually write:

struct structure_name variable_name;

The prefix ‘struct’ is required for the compiler to understand that you are defining a structure variable. In C++, this is not necessary; you can simply use: structure_name variable_name.

struct student 
{
  char name[20];
  int age;
  float score; 
};
int main (void)
{
  student stu = {"wit", 20, 99};
  return 0;
}

If we use typedef, we can declare an alias for student as student_t and a pointer type for the structure as student_ptr. This allows us to directly use student_t to define a structure variable without needing to write ‘struct’, making the code cleaner.

#include <stdio.h>
typedef struct student
{
  char name[20];
  int  age;
  float score;
}student_t, *student_ptr;

int main (void)
{
  student_t   stu = {"wit", 20, 99};
  student_t  *p1 = &stu;
  student_ptr p2 = &stu;
  printf ("name: %s\n", p1->name);
  printf ("name: %s\n", p2->name); 
  return 0;
}

The program output is:
wit
wit

1.2 Combining typedef with Arrays

typedef can also be used with arrays. To define an array, we usually write int array[10];. We can also use typedef to declare an array type first and then use this type to define an array.

typedef int array_t[10]; 
array_t array;
int main (void)
{
  array[9] = 100;
  printf ("array[9] = %d\n", array[9]);
  return 0;
}

In the above demo program, we declared an array type array_t, and then used that type to define an array array, which effectively is the same as int array[10].

1.3 Combining typedef with Pointers

typedef char * PCHAR;
int main (void)
{
    //char * str = "Learning Embedded";
  PCHAR str = "Learning Embedded";
  printf ("str: %s\n", str);
  return 0;
}

In the above demo, PCHAR’s type is char *, and we use PCHAR to define a variable str, which is essentially a char * pointer.

1.4 Combining typedef with Function Pointers

To define a function pointer, we typically use the following form:

int (*func)(int a, int b);

We can also use typedef to declare a function pointer type: func_t

typedef int (*func_t)(int a, int b);
func_t fp;  // Define a function pointer variable

Let’s write a simple program to test it, and it runs OK:

typedef int (*func_t)(int a, int b);
int sum (int a, int b)
{
  return a + b;
} 
int main (void)
{
  func_t fp = sum;
  printf ("%d\n", fp(1,2));
  return 0;
}

To improve code readability, we often see the following declaration form:

typedef int (func_t)(int a, int b);
func_t *fp = sum;

Functions have types, and we use typedef to give a new name to the function type: func_t. The benefit of this declaration is that even if you do not see the definition of func_t, you can clearly know that fp is a function pointer, making the code more readable than the previous example.

1.5 Combining typedef with Enumerations

typedef enum color
{
  red,
  white,
  black,
  green,
  color_num,
} color_t;

int main (void)
{
  enum color color1 = red;
  color_t    color2 = red;
  color_t color_number = color_num;
  printf ("color1: %d\n", color1);
  printf ("color2: %d\n", color2);
  printf ("color num: %d\n", color_number);
  return 0;
}

The method of combining enumerations with typedef is similar to that with structures: you can use typedef to give the enumeration type color a new name color_t, and then directly use this type to define an enumeration variable.

2. Advantages of Using typedef

Different projects have different code styles and “quirks”. After reading enough code, you will find that some code prefers macros while others prefer using typedef. So what are the benefits of using typedef? Why do many people like to use it?

2.1 Makes Code Clearer and More Concise

typedef struct student
{
  char name[20];
  int  age;
  float score;
}student_t, *student_ptr;

student_t   stu = {"wit", 20, 99};
student_t  *p1 = &stu;
student_ptr p2 = &stu;

As shown in the example code, by using typedef, we can omit the struct keyword when defining a structure, union, or enumeration variable, making the code more concise.

2.2 Increases Code Portability

The int type in C can have different storage lengths depending on the compiler and platform: it could be 2 bytes, 4 bytes, or even 8 bytes. If we want to define a fixed-length data type in our code, using int may lead to issues when running on different platforms. The best way to deal with various compiler “quirks” is to use custom data types instead of C’s built-in types.

#ifdef PIC_16
typedef  unsigned long U32
#else
typedef unsigned int U32  
#endif

In a 16-bit PIC microcontroller, int generally occupies 2 bytes, and long occupies 4 bytes, while in a 32-bit ARM environment, both int and long generally occupy 4 bytes. If we want to use a 32-bit fixed-length unsigned type in our code, we can declare a U32 data type as shown above. You can confidently use U32 in your code. When porting the code to different platforms, you just need to modify this declaration.

In the Linux kernel, drivers, and BSPs that are closely related to low-level architecture, we often see such data types like size_t, U8, U16, U32. In network protocols, network card drivers, and other areas where byte width and endianness are of concern, typedef is frequently used.

2.3 Better Than Macro Definitions

The C preprocessor directive #define is used to define a macro, while typedef is used to declare an alias for a type. Compared to macros, typedef is not just a simple string replacement; it allows you to define multiple objects of the same type simultaneously.

typedef char* PCHAR1;
#define PCHAR2 char *

int main (void)
{
  PCHAR1 pch1, pch2;
  PCHAR2 pch3, pch4;
  printf ("sizeof pch1: %d\n", sizeof(pch1));
  printf ("sizeof pch2: %d\n", sizeof(pch2));
  printf ("sizeof pch3: %d\n", sizeof(pch3));
  printf ("sizeof pch4: %d\n", sizeof(pch4));
  return 0;
}

In the above example code, we wanted to define four pointer variables pointing to char type, but the output result was:

sizeof pch1: 4
sizeof pch2: 4
sizeof pch3: 4
sizeof pch4: 1

We intended to define four pointers to char type, but pch4, after macro expansion, became a character variable instead of a pointer variable. On the other hand, PCHAR1, as a data type, is syntactically equivalent to the same type specifier keywords, allowing us to define multiple variables in one line of code. The above code is essentially equivalent to:

char *pch1, *pch2;
char *pch3, pch4;

2.4 Simplifies Complex Pointer Declarations

Some complex pointer declarations, such as arrays of function pointers, are often very complicated and hard to read. For example, the definition of a function pointer array:

int *(*array[10])(int *p, int len, char name[]);

Many people may find the above pointer array definition confusing. We can optimize it using typedef: first declare a function pointer type func_ptr_t, and then define an array, which will be much clearer and more readable:

typedef int *(*func_ptr_t)(int *p, int len, char name[]);
func_ptr_t array[10];

3. Points to Note When Using typedef

From the above examples, we can see that using typedef can make our code cleaner and more readable. However, typedef also has its pitfalls, and a slight mistake can lead to issues. Here are some details to keep in mind when using typedef.

3.1 typedef is Syntactically Equivalent to Keywords

When we use typedef to declare an alias for a known type, it is syntactically equivalent to the type’s type specifier keyword, rather than being a simple string replacement like a macro. For example, when mixing const with types: when const is used with common types (like int, char), the positions of const and the type can be swapped. However, if the type is a pointer, const and the pointer type cannot be swapped; otherwise, the type of the modified variable will change, as seen with common constant pointers and constant pointers:

char b = 10;
char c = 20;
int main (void)
{  
  char const *p1 = &b; // Constant pointer: *p1 is immutable, p1 is mutable
  char *const p2 = &b; // Pointer constant: *p2 is mutable, p2 is immutable  
  p1  = &c; // Compiles fine 
  *p1 = 20; // error: assignment of read-only location  
  p2  = &c; // error: assignment of read-only variable`p2'
  *p2 = 20; // Compiles fine
  return 0;
}

When const is used with typedef to modify a pointer type, comparing it to a macro-defined pointer type:

typedef char* PCHAR2;
#define PCHAR1 char * 
char b = 10;
char c = 20;
int main (void)
{  
  const PCHAR1 p1 = &b;
  const PCHAR2 p2 = &b;
  p1  = &c; // Compiles fine 
  *p1 = 20; // error: assignment of read-only location  
  p2  = &c; // error: assignment of read-only variable`p2'
  *p2 = 20; // Compiles fine
  return 0;
}

Running the program, you will find that the same compilation errors occur as in the previous example, due to the fact that macro expansion is simply a string replacement:

const PCHAR1 p1 = &b; // After macro expansion, it becomes a constant pointer
const char * p1 = &b; // Here, const and type char can swap positions

In the case of the variable p2 defined using PCHAR2, PCHAR2 acts as a type, allowing it to swap positions with const, meaning that const modifies the value of pointer variable p2, which cannot change, making it a pointer constant, but the value of *p2 can change.

const PCHAR2 p2 = &b; // PCHAR2 here acts as a type, and can swap position with const
PCHAR2 const p2 = &b; // This statement is equivalent to the previous one
char * const p2 = &b; // Here, const modifies p2!

3.2 typedef is a Storage Class Keyword

Surprisingly, typedef is syntactically a storage class keyword! Like common storage class keywords (such as auto, register, static, extern), when modifying a variable, you cannot use more than one storage class keyword simultaneously, or the compilation will fail:

typedef static char * PCHAR;
//error: multiple storage classes in declaration of `PCHAR'

3.3 Scope of typedef

Compared to the global nature of macros, typedef, as a storage class keyword, has a scope. The types declared with typedef follow the same scope rules as regular variables, including block scope and file scope.

typedef char CHAR;

void func (void)
{
  #define PI 3.14
  typedef short CHAR;
  printf("sizeof CHAR in func: %d\n",sizeof(CHAR)); 
}

int main (void)
{  
  printf("sizeof CHAR in main: %d\n",sizeof(CHAR));
  func();
  typedef int CHAR;
  printf("sizeof CHAR in main: %d\n",sizeof(CHAR));
  printf("PI:%f\n", PI);  
  return 0;
}

Macro definitions are replaced during preprocessing and are global; they can be referenced as long as their definition appears before use. In contrast, types declared using typedef follow the same scope rules as regular variables. The output of the above code will be:

sizeof CHAR in main: 1
sizeof CHAR in func: 2
sizeof CHAR in main: 4
PI:3.140000

4 How to Avoid Misusing typedef?

From the above study, we can see that using typedef can make our code cleaner and more readable. In actual programming, more and more people are trying to use typedef, sometimes to the point of misuse: whenever encountering a structure, union, or enumeration, they feel the need to wrap it in typedef, making it seem like if you don’t use it, your code is inferior.

However, typedef also has side effects; it is not always necessary to use it everywhere. For example, when you define a variable of the STUDENT type we wrapped,

STUDENT stu;

Without looking at the declaration of STUDENT, do you know what stu means? Probably not. If we directly use struct to define a variable, it would be much clearer, allowing you to immediately know that stu is a variable of struct type:

struct  student stu;

Generally speaking, using typedef may be beneficial in the following situations, otherwise it may have the opposite effect:

  • Creating a new data type

  • Cross-platform, fixed-length types: such as U32/U16/U8

  • Data types related to operating systems, BSPs, and network byte widths: such as size_t, pid_t, etc.

  • Opaque data types: data types that need to hide structure details and can only be accessed through function interfaces

When reading Linux kernel source code, you will find extensive use of typedef, even for simple int and long. This is because the Linux kernel source has evolved to support many platforms and CPU architectures. To ensure data cross-platform compatibility and portability, typedef is often used to designate fixed lengths for some data: such as U8/U16/U32, etc. However, this does not mean that typedef is overused; there are certain rules to follow regarding when to use it and when not to, which can be found in the kernel documentation’s CodingStyle regarding typedef usage recommendations.

Source: NetworkCopyright belongs to the original author. If there is any infringement, please contact for deletion.

end

Previous Recommendations

Essential Readings for Embedded Linux

Recommended Learning Path for Embedded Systems

A Reader’s Clear Logic in Questions

Successful Transition to Embedded from Mechanical Engineering

A Reader’s Experience in the Audio-Video Direction During Autumn Recruitment

Why Embedded C Programming Prefers Typedef?Why Embedded C Programming Prefers Typedef?

Scan to Add Me on WeChat

Join the Technical Group

Why Embedded C Programming Prefers Typedef?

Why Embedded C Programming Prefers Typedef?

Share

Why Embedded C Programming Prefers Typedef?

Collect

Why Embedded C Programming Prefers Typedef?

Like

Why Embedded C Programming Prefers Typedef?

View

Leave a Comment