typedef几种经典用法

介绍几种typedef与结构体、联合体、枚举、函数指针声明结合使用的经典用法。

一、typedef的基本用法

以结构体类型的声明和使用为例,C语言提供了struct关键字来定义一个结构体类型。

struct student
{
    char name[20];
    int age;
    double score;
};
struct student stu1 = {"lily", 20 , 99}; /* C语言中 */
student stu2 = {"rose", 21 , 89}; /* C++中 */

 我们可以使用typedef关键字,给student声明一个别名student_t和一个结构体指针类型student_ptr,然后就可以直接使用student_t类型去定义一个结构体变量,不再写struct。

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

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

typedef除了可以和结构体结合使用,还可以与数组结合使用。定义一个数组,通常使用int array[10]即可。我们可以使用typedef先声明一个数组类型,然后使用这个类型去定义一个数组。

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

typedef还可以与指针结合使用。

typedef char *PCHAR;

int main(void)
{
    //char *str = "升职加薪!";
    PCHAR str = "升职加薪!";
    printf("str:%s\n", str);
    return 0;
}

typedef还可以与函数指针结合使用。

typedef int (*func)(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;
}

为了增加函数可读性,我们更建议用func_t *fp = sum;这样即使你没有看到func_t的定义,也能清楚知道fp是一个函数指针。

typedef还可以与枚举结合使用。

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;
}

二、typedef的优势

1、让代码更加清晰简洁

在定义一个结构体、联合、枚举变量时,省去关键字struct,让代码更加简捷。

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 = &stu;

2、增加代码的可移植性

在不同的编译器和平台下,所分配的存储字长不一样。为了应付各种编译器,最好的办法就是使用自定义数据类型,而不是使用C语言的内置类型。

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

在16位的PIC单片机中,int型一般占2字节,long型占4字节,而在32位的ARM环境下,int型和long型一般都是占4字节。若我们在代码中想使用一个32位的固定长度的无符号类型数据,则可以使用上面的方式声明一个U32的数据类型,在程序中就能放心大胆使用U32.

在Linux内核、驱动、BSP等与底层架构平台密切相关的源码中,我们会经常看到size_t、U8、U16、U32等这样的数据类型。在一些网络协议、网卡驱动等对字节宽度、大小端比较关注的地方,也会经常看到typedef被频繁使用。

3、比宏定义更好用

C语言预处理指令#define用来定义一个宏,而typedef则用来声明一种类型的别名。typedef和宏相比起来,不仅只是简单的字符串替换,而是可以使用该类型同时定义多个同类型对象。

举例:

typedef char *PCHAR1;
#define PCHAR char *;

int main(void)
{
    PCHAR1 pch1, pch2;
    PCHAR2 pch2, 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;
}

在这段代码里,我们想定义4个指向char类型的指针变量,然而运行结果为:

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

 pch4经过预处理宏展开后,变成了一个字符型变量,而不是一个指针变量。而经过typedef定义的PCHAR1在语法上等价于相同类型的类型说明符关键字,因此可以在一行代码中定义多个变量。上端代码等价于:

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

4、让复杂的指针声明更加简洁

一些复杂的指针声明,如函数指针、数组指针、指针数组的声明,往往很复杂,可读性差。如:

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

我们使用typedef将其优化一下:先声明一个函数指针类型func_ptr_t,接着定义一个数组,就会大大增加其可读性。

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

三、使用typedef的注意事项

typedef在语法上等价于C语言的关键字。我们使用typedef为已知的类型声明一个别名,在语法上其实就等价于该类型的类型说明符关键字,而不是像宏一样,仅仅是简单的字符串替换。

举例说明:

如const和类型的混合使用。当const和常见的类型(如int、char)共同修饰一个变量时,const和类型的位置可以互换。但是如果类型为指针,则const和指针类型不能互换,否则其修饰的变量类型就发生了变化。

char b = 10;
char c = 20;
int main(void)
{
    char const *p1 = &b;//常量指针:*p1不可变,p1可变
    char *const p2 = &b;//指针常量:*p2可变,p2不可变
    p1 = &c;//编译正常
    *p1 = 20;//error:assignment of read-only location
    p2 = &c;//error:assignment of read-only variable p2
    *p2 = 20;//编译正常
    return 0;
}

当typedef和const一起修饰一个指针类型时,与宏定义的指针类型进行比较。

typdef char *PCHAR2;
#define PCHAR1 char *;

char b = 10;
char c = 20;

int main(void)
{
    const PCHAR1 p1 = &b;
    const PCHAR2 p2 = &b;
    p1 = &c;//编译正常
    *p1 = 20;//error:assignment of read-only location
    p2 = &c;//error:assignment of read-only variable p2
    *p2 = 20;//编译正常
    return 0;
}

运行程序,这两段代码会遇到相同的编译错误,原因在于宏展开中仅仅是简单的字符串替换。

其次,typedef也是一个存储类关键字。和常见的存储类关键字,如auto、register、static、extern一样,不能使用一个以上的存储类关键字,否则编译会报错。

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

四、typedef的作用域

使用typedef声明的类型和普通变量一样,都遵循作用域规则。

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;
}

运行结果为:

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

五、总结

当遇到以下情形时,使用typedef会更合适:

1、创建一个新的数据类型

2、跨平台的指定长度的类型,如U32/U16/U8

3、与操作系统、BSP、网络字宽相关的数据类型,如size_t、pid_t等

4、不透明的数据类型,需要隐藏结构体细节,只能通过函数接口访问的数据类型

具体大家可以看Linux Kernel Documentation目录下的CodingStyle文件中关于typedef的使用建议。

你可能感兴趣的:(C语言进阶,c++)