套娃理解c语言指针
虽说是c语言指针,对于c++也是一样的
编程基于套娃
————沃·兹基硕得
前言
这篇文章的目的并非解释什么是指针,而是为了让人能更好的理解如这样的char **(*(*p[4][10])(int,void(*)(int)))(int)
(没错上面这坨东东是合法的!)
以及,这个方法在处理一个(返回类型为函数的指针)的函数的指针的时候还是会有点混乱……需要一些悟性puq
但是考虑到这句话拿嘴说估计都能绕晕一堆人……还算情有可原?QAQ
基本定义
抽象类型
首先,我们需要抽象一下类型。
1 | template<typename T> |
也就是说,将所有的目前已知类型(不管是int、char、int*、long long[10],甚至一个函数),这里都视为一个最基本的已知类型T
于是,在整个过程中,我们便将类型简化到只剩两种:
T
T
也就是抽象出来的类型
T1 (*)(T2,T3,…)
T1 (*)(T2,T3,…)
指一个返回类型为T1,传入参数类型为(T2,T3,…)的函数指针
为什么?
这样,便可以不再纠结于具体的类型(毕竟你甚至可以通过typedef、class来得到无穷多种类型)
而只要关注于怎么解开这层套娃
基本操作
在这样的前提下,我们能得到以下几种操作:
T[]
需要注意的是:此处是没有高维数组的!
这是指建立一个T类型的数组
()
这是指提高一个优先级
*
这是指变为T类型的指针
分析步骤
step1:从中心,先向右,再向左
-
中心:当前的变量名、类型名等
特殊情况:有些时候,例如函数指针的传入参数中的某个类型,是没有名称的。
以及,记得将函数视作一个整体。 -
先向右:从中心向右,对每一个
[]
加括号,直到遇到一个右括号(因为[]总在中心的右边) -
再向左:再从中心向左,对每一个
*
加括号,直到遇到一个左括号(因为*总在中心的左边)
请允许我以这个东西为例(你并不需要知道这个东西的具体意思):
-
中心:名称n
1
int ***n[10][10]
先向左:再向右:1
2int ***(n[10])[10]
int ***((n[10])[10])1
2int **(*((n[10])[10]))
int *(*(*((n[10])[10])))
step2:剥离,定义
-
剥离:将最中间的括号取出来
(最中间的括号一般指带变量名、类型名等的那个括号) -
定义:将剩下的东西定义为一个新类型
继续以上面那个东西为例:
-
1
int *(*(*((n[10])[10])))
剥离中间括号:
1
t1 (n[10])
再定义类型:
1
2typedef int*(*(*([10]))) t1;
t1 n[10];你或许发现这里报错了,但是不怕,最后会一起处理。
step3:改写处理的结果,使其符合语法规范
为了让整个过程保持一致,上面的某些语法是不符合规范的。对此,我们需要一些改写,具体有以下几种:
去除所有多添加的括号
比如,int*(*)
是不被c++所许可的,我们需要改成int**
将数组分为应有的形式
比如,typedef t2[10] t1;
的意思是:t1所指类型是一个有着10个t2元素的数组。我们需要写成这样:typedef t2 t1[10];
,既把数组大小放到新定义的类型名之后。
函数指针的类型名应该在它的内部
比如,typedef char**(*(*)(int, void (*)(int)))(int) t2
是不对的,t2应该放在内部,typedef char**(*(*t2)(int, void (*)(int)))(int)
改写后的例子应该长这样:
-
1
2typedef int*** t1[10];
t1 n[10];
step4:递归的处理,直到所有的东西都是你所熟悉的
继续以上面那个东西为例:
-
1
2
3typedef int*** t2;
typedef t2 t1[10];
t1 n[10];你看,现在就很清楚了。1
2
3
4typedef int** t3
typedef t3* t2;
typedef t2 t1[10];
t1 n[10];
step5:简化(可选)
你看上面有一个
1 | typedef t2 t1[10]; |
但其实写成t2 n[10][10]
,我们也能看的懂,那就可以简化了。
验证
我们不妨来验证一下:
ide认为他俩是一样的
解决开始那坨复杂的东西
现在,我们不妨来尝试解决掉这一坨东西吧:
char (*(*p[4][10])(int,void(*)(int)))(int)
接下来是一步步解开它的过程:
1 | char **(*(*p[4][10])(int,void(*)(int)))(int); |
1 | ```c++ |
1 | typedef char**(*(*t2)(int,void(*)(int)))(int); |
1 | typedef void(*t3)(int); |
1 | typedef void (*t3)(int); |
1 | typedef void (*t3)(int); |
1 | typedef char** (*t5)(int); |
于是我们终于看清了它的面目:
p是二维数组,其中的元素是传入参数为(int,t3),返回值为t5的函数的指针
t3是一个传入(int),无返回值的函数的指针
t5是一个传入(int),返回值为指向char的指针的指针
最后再来把你绕晕掉!
也就是说:char **(*(*p[4][10])(int,void(*)(int)))(int)
,p的类型为:
p是元素为传入参数为(int,一个传入(int),无返回值的函数的指针),返回值为一个传入(int),返回值为指向char的指针的指针的函数的指针的二维数组
233