虽说是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:从中心,先向右,再向左

  • 中心:当前的变量名、类型名等
    特殊情况:有些时候,例如函数指针的传入参数中的某个类型,是没有名称的。
    以及,记得将函数视作一个整体。

  • 先向右:从中心向右,对每一个[]加括号,直到遇到一个右括号(因为[]总在中心的右边)

  • 再向左:再从中心向左,对每一个*加括号,直到遇到一个左括号(因为*总在中心的左边)

请允许我以这个东西为例(你并不需要知道这个东西的具体意思):

  • 1
    int ***n[10][10]
    中心:名称n
    先向左:
    1
    2
    int ***(n[10])[10]
    int ***((n[10])[10])
    再向右:
    1
    2
    int **(*((n[10])[10]))
    int *(*(*((n[10])[10])))

step2:剥离,定义

  • 剥离:将最中间的括号取出来
    (最中间的括号一般指带变量名、类型名等的那个括号)

  • 定义:将剩下的东西定义为一个新类型

继续以上面那个东西为例:

  • 1
    int *(*(*((n[10])[10])))

    剥离中间括号:

    1
    t1 (n[10])

    再定义类型:

    1
    2
    typedef 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
    2
    typedef int*** t1[10];
    t1 n[10];

step4:递归的处理,直到所有的东西都是你所熟悉的

继续以上面那个东西为例:

  • 1
    2
    3
    typedef int*** t2;
    typedef t2 t1[10];
    t1 n[10];
    1
    2
    3
    4
    typedef int** t3
    typedef t3* t2;
    typedef t2 t1[10];
    t1 n[10];
    你看,现在就很清楚了。

step5:简化(可选)

你看上面有一个

1
2
typedef t2 t1[10];
t1 n[10];

但其实写成t2 n[10][10],我们也能看的懂,那就可以简化了。

验证

我们不妨来验证一下:
ide验证
ide认为他俩是一样的

解决开始那坨复杂的东西

现在,我们不妨来尝试解决掉这一坨东西吧:
char (*(*p[4][10])(int,void(*)(int)))(int)
接下来是一步步解开它的过程:

1
char **(*(*p[4][10])(int,void(*)(int)))(int);
1
2
3
```c++
typedef char**(*(*t1[10])(int,void(*)(int)))(int) ;
t1 p[4];
1
2
3
typedef char**(*(*t2)(int,void(*)(int)))(int);
typedef t2 t1[10];
t1 p[4];
1
2
3
4
typedef void(*t3)(int);
typedef char*(*(*((*t2)(int, t3)))(int));
typedef t2[10] t1;
t1 p[4];
1
2
3
4
5
typedef void (*t3)(int);
typedef char* t4;
typedef t4*(*(*t2)(int, t3))(int);
typedef t2 t1[10];
t1 p[4];
1
2
3
4
5
6
7
typedef void (*t3)(int);
typedef char *t4;
typedef t4 *(*t5)(int);
typedef t5 (*t2)(int, t3);

typedef t2 t1[10];
t1 p[4];
1
2
3
4
5
typedef char** (*t5)(int);
typedef void (*t3)(int);
typedef t5 (*t2)(int, t3);

t2 p[4][10];

于是我们终于看清了它的面目:
p是二维数组,其中的元素是传入参数为(int,t3),返回值为t5的函数的指针
t3是一个传入(int),无返回值的函数的指针
t5是一个传入(int),返回值为指向char的指针的指针

最后再来把你绕晕掉!

也就是说:char **(*(*p[4][10])(int,void(*)(int)))(int),p的类型为:
p是元素为传入参数为(int,一个传入(int),无返回值的函数的指针),返回值为一个传入(int),返回值为指向char的指针的指针的函数的指针的二维数组


233