C/C++:typedef用法详解

C/C++:type用法详解

用途一:类型别名

1
2
typedef char* PCHAR;
PCHAR pa,pb;//等价于 char *pa,*pb;

用途二:struct别名

1
2
3
4
5
6
//c中不能用是struct [类名]来定义,不能只用类名,typedef可以简化代码
typedef struct{
int x;
int y;
}rectangle;
rectangle p;

用途三: 跨平台迁移(其实也就是类型别名)

例如各平台支持最高精度不同,我们可以用typedef定义与平台无关的类型

1
2
3
4
5
6
//平台一
typedef long double REAL;
//平台二
typedef double REAL;
//平台三
typedef float REAL;

在程序中只是用REAL,跨平台时可以减少代码更改数量

用途四:为复杂的声明定义一个简单别名

这部分才是重点,Linux源码中经常会看到如下定义,绝大多数人会看的一脸懵逼

1
2
3
4
5
void * (* (*fp1) (int)) [10];
float (* (*fp2) (int, int, float)) (int);
typedef double (* (* (*fp3) ()) [10]) ();
fp3 a;
int (* (*fp4()) [10]) ();

下边一步步来看,顺便了解一下右左原则,就可以顺利看懂上面的定义了

  1. 定义一个整型

int a;

  1. 定义一个指向整型的指针

int *p;

  1. 定义一个指向指针指针,它指向的指针指向一个整型

int **pp;

  1. 定义一个包含10个整型数的数组

int arr[10];

  1. 定义一个指向包含10个整型数数组指针

int (*pArr)[10];

到目前为止都还比较容易理解,写过C的多多少少都写过以上代码

  1. 定义一个指向函数指针,被指向的函数有一个整型参数并返回整型数

int (*pfunc)(int);

  1. 定义一个包含10个指针的数组,其中包含的指针指向函数,这些函数有一个整型参数并返回整型

int(*arr[10])(int);

把6、7定义串起来

1
2
3
4
5
//指向函数的指针
int (*pfunc)(int);
//包含函数指针的数组
int (*arr[10])(int);
arr[0]=pfunc;

一方面很多人没用过函数指针,逐渐开始难以理解了,这是就需要理解复杂定义的右左法则

从变量名看起,先往右,再往左,碰到圆括号就调转阅读方向;括号内分析完就跳出括号,还是先右后左的顺序,如此循环。

以(6)为例:

1
2
3
int (*pfunc)(int);
//找到变量名 pfunc,往右为圆括号,调转阅读方向,左边是个‘*’,这说明pfunc是一个 指针。
//然后跳出这个括号,先往右,遇到圆括号,这说明(*pfunc)是一个函数,所以pfunc是一个函数指针,且具有一个int类型参数,返回值类型为int

以(7)为例:

1
2
3
4
5
int (*arr[10])(int);
//找到变量名arr,先往右为是[],表示arr是一个数组,再往右遇到圆括号,调转阅读方向,左边是一个‘*’,这说明arr数组内放的是指针
//跳出圆括号,先往右为圆括号,表示指针是函数指针,并且具有一个int类型参数,返回类型为int

//即:arr是一个包含10个函数指针的数组,这个函数指针的参数为一个int变量,返回值也为int类型

具体怎么判断是函数指针、数组指针还是数组,可以抽象成以下几个模式:

  • type (var)(...); // 变量名var与结合,被圆括号括起来,右边是参数列表。表明这是函数指针
  • type (var)[]; //变量名var与结合,被圆括号括起来,右边是[]运算符。表示这是数组指针
  • type (*var[])...; // 变量名var先与[]结合,说明这是一个数组(至于数组包含的是什么,由旁边的修饰决定)

到这里,已经具备了读懂复杂定义的全部能力,回来看一下本节开头的几个复杂定义:

一、

1
2
3
4
5
6
void* (*(*fp1)(int))[10];
//找到变量名fp1,先往右为圆括号,调转阅读方向,往左为‘*’,则表示fp1为一个指针
//跳出圆括号,先往右为圆括号,表示fp1是函数指针,调转阅读方向,往左为‘*’,表示fp1为函数指针,函数参数为一个int类型,返回值为指针
//跳出圆括号,先往右为方括号,表示函数返回值为数组指针,在往左为void*,即数组内包含的类型为void *.

//即:fp1是一个指向函数的指针,这个函数接受一个整型参数,并返回含有10个(void*)类型的数组的指针
二、
1
2
3
4
5
6
float (* (*fp2) (int, int, float)) (int);
//找到变量名fp2,先往右为圆括号,调转阅读方向,向左为‘*’,表示fp为指针
//跳出圆括号,先往右为圆括号,表示fp2为函数指针,调转阅读方向,往右为‘*’,表示fp2函数指针指向的函数,有int、int、float类型的三个参数,并且返回值为指针
//跳出圆括号,先往右为圆括号,调转阅读方向,往左为float,表示返回值指针为函数指针,该函数指针接受一个int类型参数,返回一个float类型值

//即:fp2是一个指向函数的指针,这个指针接受三个参数(int,int,float),并返回一个函数指针,这个函数指针接受一个参数(int),返回一个float。
三、
1
2
3
4
5
6
7
8
typedef double (* (* (*fp3) ()) [10]) ();
//先无视typedef
//找到变量名fp3,先往右为右括号,调转阅读方向,向左为‘*’,表示fp3为一个指针
//跳出圆括号,先往右为圆括号,调转阅读方向,往左为‘*’,表示fp3是一个函数指针,这个函数指针无参数,且返回值为指针
//跳出圆括号,先往右为方括号,表示返回指针为数组指针,再往右为‘*’,表示数组内包含的为指针
//跳出圆括号,先往右为圆括号,表示数组内的指针为函数指针,该指向的函数参数列表为空,再往左为double,表示该函数返回值类型为double

//即:fp3为一个无参的函数指针,该函数的返回值为包含10个元素的数组指针,该数组内的元素为函数指针,数组内的函数指针不接收参数并返回double值
这个比前面两个复杂案例多了一个typedef修饰,表示fp3并不是一个真正的函数指针,而是一个别名,可以用来定义其他同类的函数指针,如fp3 a

四、

1
2
3
4
5
6
int (* (*fp4()) [10]) ();
//先找到变量名fp4,先往右为圆括号,调转阅读方向为‘*’,表示fp4为函数,返回值为指针
//跳出圆括号,先往右为方括号,表示返回的指针为数组指针,往右为‘*’,表示该数组内包含的元素为指针
//跳出圆括号,先往右为圆括号,表示数组内指针为函数指针(无参),再往右为int,表示返回值为int

//即:fp4为无参函数,返回包含10个函数指针的数组的指针,数组内的函数指针无参,返回值为int类型
到这里基本就可以看懂源码里面那些复杂的定义了,真心佩服那些巨佬。

参考资料