目录
内容与参考资料基本一致,稍微修改了一点参考资料上存在的错误
一、typedef的四种用法
1. 定义类型别名
char *a, *p;
typedef char* char_pointer;
char_pointer pa, pb;
通过定义类型别名在声明多个指针变量时就方便了很多,也减少了漏写星号*
的风险。
2. typedef struct
这个实际上在C语言中比较常用到,在C语言中如果要声明一个struct
对象必须使用struct [结构名][对象名]
的语法来实现:
struct A {
int i;
int j;
};
struct A a;
使用typedef
可以简化这一步骤直接使用[结构名][对象名]
来声明:
typedef struct B {
int i;
int j;
}B;
B b;
不过在C++中不需要typedef
也能直接使用结构名来声明对象。
在没有typedef
的情况下末尾定义的是变量,有情况下则是类型别名。
struct Human{
int age;
} bob; // bob是一个变量
typedef struct student{
int age;
} Stu; // Stu是结构体student的别名
3. 定义和平台无关的数据类型
比如定义一个叫REAL
的浮点类型,表示为目标平台上最高精度的类型:
typedef long double REAL;
// 如果不支持long double 改为:
typedef double REAL;
// 如果不支持 double 改为:
typedef float REAL;
也就是说在跨平台时遇到不支持的类型情况只需要修改typedef
一处即可,不需要大量修改源码的其他地方。
标准库中广泛使用了这一技巧,比如size_t
4. 为复杂的声明顶一个简单别名
例如函数指针相关的
int *(*a[5])(int, char*); // 原声明
typedef int *(*fun_ptr)(int, char*);
fun_ptr a[5];
在原来的声明里逐步用别名替换一部分复杂声明,如此循环,
把带变量名的部分留到最后替换,得到的就是原声明的最简化版。
void (*b[10])(void(*)()); // 原声明
// 先替换右边括号
typedef void (*fun_ptr_param)();
// 再替换左边括号
typedef void (*fun_ptr)(fun_ptr_param);
// 原声明简化后
fun_ptr p[10];
参考的文章中在第四行的代码为:
typedef void (*pFunParam);
这实际上是有问题的,它不是函数别名而是
void *
类型的别名
二、如何理解复杂声明和定义
参考资料给了一个
中的例子,但我其实感觉例子不是很好,而且在我自己电脑上尝试的时候会报错。需要使用 ::Q
,所以这部分代码就不贴了。有兴趣的看参考资料原文吧。
在阅读Linux的内核代码时经常会遇到一些复杂的声明和定义,例如:
void * (* (*fp1) (int)) [10];
float (* (*fp2) (int, int, float)) (int);
typedef double (* (* (*fp3) ()) [10]) ();
fp3 a;
int (* (*fp4()) [10]) ();
要理解这些复杂的声明和定义,应该由浅而深,逐步突破。
下面先看一些简单的定义:
int a; //定义一个整型数
int *p; //定义一个指向整型数的指针
int **pp; //定义一个指向指针的指针,它指向的指针指向一个整型数
p = &a; // p指向整数a所在的地址
pp = &p; // pp指向指针p
如下所示,定义整数型数组的指针指向整数型数组。
int arr[10]; //定义一个包含10个整型数的数组
int (*pArr) [10]; //定义一个指向包含10个整型数数组的指针
pArr = &arr;
如下所示,包含指向函数的指针的数组,这些函数有整型参数和整型返回值。
int (*pfunc) (int); //定义一个指向函数的指针,被指向的函数有一个整型参数并返回整型值
int (*arr[10]) (int); //定义一个包含10个指针的数组,其中包含的指针指向函数,
//这些函数有一个整型参数并返回整型值
arr[0] = pfunc;
三、右左法则
当声明和定义逐渐复杂时,需要使用用于理解复杂定义的右左法则:
从变量名看起,先往右,再往左,碰到圆括号就调转阅读的方向;括号内分析完就跳出括号,还是先右后左的顺序。如此循环,直到分析完整个定义。
用分析int (*pfunc) (int)
为例
- 找到变量名
pfunc
,先往右是圆括号,调转方向,左边是一个*
号,这说明pfunc
是一个指针; - 然后跳出这个圆括号,先看右边,又遇到圆括号,这说明
(*pfunc)
是一个函数,所以pfunc
是一个指向这类函数的指针,即函数指针, - 这类函数具有一个
int
类型的参数,返回值类型是int
。
同样的,对于int (*arr[10]) (int)
- 找到变量名
arr
,先往右是[]
运算符,说明arr
是一个数组; - 再往左是一个
*
号,说明arr
数组的元素是指针(注意:这里的*
修饰的不是arr
,而是arr[10]
。原因是[]
运算符的优先级比*
要高,arr
先与[]
结合。); - 跳出圆括号,先往右又遇到圆括号,说明
arr
数组的元素是指向函数的指针,它指向的函数有一个int
类型的参数,返回值类型是int
。
那么,怎么判断定义的是函数指针,还是数组指针,或是数组呢?可以抽象出几个模式:
typedef (*var)(...); // 变量名var与*结合,被圆括号括起来,右边是参数列表。表明这是函数指针
typedef (*var)[]; //变量名var与*结合,被圆括号括起来,右边是[]运算符。表示这是数组指针
typedef (*var[])...;// 变量名var先与[]结合,说明这是一个数组(至于数组包含的是什么,由旁边的修饰决定)
下面可以利用右左法则去分析复杂的声明和定义:
void * (* (*fp1)(int)) [10];
- 找到变量名
fp1
,往右看是圆括号,调转方向往左看到*
号,说明fp1
是一个指针; - 跳出内层圆括号,往右看是参数列表,说明
fp1
是一个函数指针,接着往左看是*
号,说明指向的函数返回值是指针; - 再跳出外层圆括号,往右看是
[]
运算符,说明函数返回的是一个数组指针,往左看是void *
,说明数组包含的类型是void *
。
简言之 ,fp1
是一个指向函数的指针,该函数接受一个整型参数并返回一个指向含有10个void
指针数组的指针。
float (* (*fp2) (int, int, float)) (int);
- 找到变量名
fp2
,往右看是圆括号,调转方向往左看到*
号,说明fp2
是一个指针; - 跳出内层圆括号,往右看是参数列表,说明
fp2
是一个函数指针,接着往左看是*
号,说明指向的函数返回值是指针; - 再跳出外层圆括号,往右看还是参数列表,说明返回的指针是一个函数指针,该函数有一个
int
类型的参数,返回值类型是float
。
简言之,fp2
是一个指向函数的指针,该函数接受三个参数(int, int, float)
,且返回一个指向函数的指针,该函数接受一个int
参数并返回一个float
。
typedef double (* (* (*fp3) ()) [10]) ();
fp3 a;
如果创建许多复杂的定义,可以使用typedef。这一条显示typedef是如何缩短复杂的定义的。
- 跟前面一样,先找到变量名
fp3
(这里fp3
其实是新类型名),往右看是圆括号,调转方向往左是*
,说明fp3
是一个指针; - 跳出圆括号,往右看是空参数列表,说明
fp3
是一个函数指针,接着往左是*
号,说明该函数的返回值是一个指针; - 跳出第二层圆括号,往右是
[]
运算符,说明函数的返回值是一个数组指针,接着往左是*
号,说明数组中包含的是指针; - 跳出第三层圆括号,往右是参数列表,说明数组中包含的是函数指针,这些函数没有参数,返回值类型是
double
。
简言之,fp3
是一个指向函数的指针,该函数无参数,且返回一个含有10个指向函数指针的数组的指针,这些函数不接受参数且返回double
值。
这二行接着说明:a是fp3类型中的一个。
int (* (*fp4()) [10]) ();
这里fp4
不是变量定义,而是一个函数声明。
- 找到变量名
fp4
,往右是一个无参参数列表,说明fp4是一个函数,接着往左是*
号,说明函数返回值是一个指针; - 跳出里层圆括号,往右是
[]
运算符,说明fp4
的函数返回值是一个指向数组的指针,往左是*
号,说明数组中包含的元素是指针; - 跳出外层圆括号,往右是一个无参参数列表,说明数组中包含的元素是函数指针,这些函数没有参数,返回值的类型是
int
。
简言之,fp4
是一个返回指针的函数,该指针指向含有10个函数指针的数组,这些函数不接受参数且返回整型值。
四、用typedef简化复杂的声明和定义
-
int *(*a[10]) (int, char*);
用前面的“右左法则”,可以知道
a
是一个包含10
个函数指针的数组,这些函数的参数列表是(int, char*)
,返回值类型是int *
。- 如果要定义相同类型的变量
b
,都得重复书写:int *(*b[10]) (int, char*)
; - 为了避免重复复杂的定义,用
typedef
来简化复杂的声明和定义。typedef
可以给现有的类型起个别名。这里用typedef
给以上a
、b
的类型起个别名:
typedef int *(*A[10])(int, char*);// 在之前定义的前面加入typedef,然后将变量名a替换成类型名A
现在要再定义相同类型的变量
c
,只需要:A c
; -
void (*b[10]) (void (*)());
-
先替换右边括号里面的参数,将
void (*)()
的类型起个别名pParam
:typedef void (*pParam) ();
-
再替换左边的变量
b
,为b的类型起个别名B
:typedef void (*B)(pParam);
-
原声明的简化版:
B b[10];
-
参考资料:
C++ typedef的详细用法 - 知乎 (zhihu.com)
标签:10,typedef,函数,int,定义,用法,指针 From: https://www.cnblogs.com/Larcvz/p/17286042.html