目录
前言
本篇文章的内容仍然为指针进阶的相关内容,继续上一篇文章的内容。 【C语言进阶】——指针(一) (字符指针,数组指针,指针数组)
指针令人头秃!!!!!!!!!
5、函数指针
我们创建函数的时候,就会在内存中开辟一块空间,既然占用了内存空间,那就有对应的内存空间地址。
函数指针,顾名思义就是指向函数的指针。
注意:
& 函数名 和 函数名均表示函数的地址!
数组名 != &数组名 函数名 == &函数名
我们怎么把函数的地址存起来呢
**注意 :**函数值指针的类型需要和返回函数值类型相同
就像这样:
思考:函数指针如何使用呢?
通过函数指针,我们可以找到函数,然后去调用这个函数。 函数指针是 &
函数名,而我们函数调用的时候可以直接使用函数名,那么这里通过函数指针调用函数也可以这样写:
或
既然这个地方的 * 可以省略,那么我们在使用的时候 * 可以用多个,也可以不要, * 号在这里就是一个摆设,这个地方放 * 是为了方便理解 + 学习指针。
阅读两个有意思的(阴间)代码:
这两个代码均是书籍《C陷阱与缺陷》中提及的内容,推荐阅读这本书。
这两个代码怎么阅读和理解呢?
**1.**先将 void () ()理解清楚,这个是一个函数指针,指针指向的函数返回类型是void的。
**2.**再理解(void () ())0 我们之前的学习中学到过强制类型转换,需要将强制转换之后的类型用括号()括起来,这个地方就是将 0 强制类型转换成void() ()类型。 为什么要将0强制类型转换成void() ()类型呢? 原因:想要将0当做某个函数的地址
深入扩展:如果一个数字想要当作一个地址,直接使用这个数字肯定是不行的,而是要将这个数字转换成也给地址编号的类型。这也是代码1中为什么要将0强制类型转换的原因。
**3.接着再看((void () ())0),对一个指针加, 就是对其进行解引用操作,对函数指针解引用就是找到这个函数
4.((void () ( ))0)( ) ,((void () ())0)找到函数后,对其使用(), 就是调用函数,所以((void () ())0)(); 是一个函数调用。 整体理解下来就是:将0强制类型转换成一个函数指针void(* )( ),再通过对这个函数指针进行解引用操作,找到这个函数,对其进行调用!
1.signal是一个函数声明
2.
signal函数的参数有两个,第一个是int类型,第二个是函数指针
,该函数指针指向的函数的参数是int类型
,返回类型是void(void(*)(int))
3.signal的返回类型是一个函数指针 因为 void(*signal XXX),该函数指针指向的函数的参数是int类型,返回类型是void(void( )(int) )
这种形式看起来就比较复杂和难以理解,我们可以用typedef类型重定义对其进行简化:
在之前的学习中,我们使用过 typedef 来定义过无符号整型 typedef unsigned int u_int ;
但是我们并没有学过指针类型如何进行类型重定义,比如说 void( )(int) ,如果我们要将其进行重定义,可以写成:
typedef void( )(int) pfun_t; 这种形式吗?
我们尝试将其放到编译器下,就会发现编译器报错,显然这种方式是行不通的!
思考:那么可以将这个类型重定义后的名称类似与定义函数指针一样放到(* )里面吗?也就是typedef void(*pfun_t)(int);
写成这种形式后,编译器没有在报错或者警告,说明这种方式是对的,实际上正确的书写方式也正是这样!
完整应该是这样书写:
虽然将一行代码变成了两行,但是简化后的代码更便于阅读和理解。
typedef 在进行类型重定义的时候,如果是函数指针类型,那么名称需要放到* 旁边,也就是说
不能写成这种形式:typedef void(*)(int) pfun_t;
正确的形式是:typedef void(*pfun_t)(int);
深入扩展:当函数的返回类型是一个函数指针的时候,函数名需要放到函数返回类型-- - 函数指针内部,而不是直接放到返回类型— 函数指针后面。
void( signal(int, void( )(int)) )(int);