Osheep

时光不回头,当下最重要。

C:函数指针的坑

关于该死的函数指针和指针函数

先来个目录

常量指针、指针常量

数组指针、指针数组

函数指针、指针函数

1、先看第一组

int const *p;// 常量指针,读作:指向常量的指针

int *const p;// 指针常量,读作:是常量的指针

2、第二组

int (*p)[5];// 数组指针,读作:指向数组的指针

int *p[5];// 指针数组,读作:元素是指针的数组

3、最后一组

int (*fun)(void );// 函数指针,读作:指向函数的指针

int *fun(void );// 指针函数,读作:返回值是指针的函数

好了,看完上面的,是不是很想吐

我们就干脆恶心到底

一、仔细分析第一组,论类型,我们从前看int const *和int *const都是整形指针。再看变量,此时我们从变量名开始看,*p我们理解时是向p指向的内存取值,再加上const,就变成了我们用指针取值是常量,就是指向了常量。而const p则是p保存的内容为常量,即地址为常量,也就是这个指针是常量。

二、再看第二组,

首先我们要对指针(*)和数组([])这两个符号的优先级补充一下,关于地址操作的两个符号(*(解引用)&(取址))都是单目运算符,而[]操作是初等运算符,初等运算符的优先级是高于单目运算符的,所以我们常常定义的指针数组int *p[5]结合起来应该是int *(p[5])。

然后,我们还有一个在理解指针时的bug,int *p喜欢写成int* p,我们理解的理所当然:“定义指针,数据类型是int*而不是int“。再结合这里我们把int *(p[5])看成int* (p[5])

最后,其实在民间,数组可以这样理解,int p[5] –>int[5] p,是不是很JAVA。

总结上面三点的理解我们的int *p[5]变成了int* (p[5]),数据类型是大小为5个int*、命名为p的数组。

然后int (*p)[5]也就是 int[5]* p,数据类型是一个指向(数据类型是大小为5个int、没有命名的数组)的指针。

真的好恶心,,,想吐


在讲第三组前,乱入一个东西,巩固一下上面两组。

typedef

不用想太多,既然我在这里强调,一定是很恶心的

typedef int (*pT)[5];

int a[][5]={1,2,3,4,5,6,7,};

pT b = a;

printf(“%d\n”,b[1][0]);

是不是很惊悚,还有这种操作!。。。

我不多说了,脑补一下也就知道了它的作用


三、我们再回到正题,讲第三组

其实第三组从内容上,并没有什么太特殊的。

所以后面,,,我准备了点小惊喜: )…..

我们都知道,变量名前面加类型,表示这个变量返回的是什么类型的数据。例如:加指针,表示返回指针。

变量后加括号,表示这是一个函数名。

int (*fun)(void )这个样子确实不太好理解,我们知道指针和数组是有时可以互换的,我们变一下:int(fun[5])(void)。这样是不是好想多了,假定int A()是一个函数,那么我们把A换成a[5]就变成了五个用数组标号的函数。

那么,我们就可以把int (*fun)(void)中的fun理解成一个指针,一个指向(返回值是int、参数是void)函数的指针。只不过这里的函数是匿名的。

是不是看到这里恍然大悟,,,

然后我们也就很简单的理解

typedef int(*funT)(void);指的是定义了一个新的数据类型,就是上面的那种。。。。。。

放心吧,我还有更坑更恶心的

( * ( void ( * ) ( ) ) 0 ) ( );

是不是很意外很惊喜!!!

吃鸡。。。。。

我们说一下它的来源,出自

《C:函数指针的坑》

图片发自《C陷阱与缺陷》

我们先回忆一下之前的知识点,当我们使用

typedef int(*funT)(void);

funT myFun = &function1;

可以看到我们生命了一个函数指针,并且指向了function1函数

那么我们该怎么使用myFun呢

定义指针时,

int *p;

p = &a;

b = *p;

所以,我们使用时,也是

(*myFun)();

所以,回到(* (void(*)()) 0)()

我们先忽略(void(*)()),因为是个强制转换我们先看其他,还剩下(*0)(),这个好理解吧,我们本来是要取址,只不过这里的址为0。但是我们要知道,0是没有数据类型的,我们取址出来的是不能使用的,所以就有了上面强制转换的部分,是不是很快就发现不要那个void其实和(*0)()的数据类型是一样的。正是如此,我们就是需要这种类型的指针。

再补上我们书上作者留的后言

《C:函数指针的坑》

图片发自《C陷阱与缺陷》

最后补充一点:ANSI C允许我们将(*fp)()简写为fp(),所以上文有的地方为了方便理解就可能使用了生僻的用法。

点赞