C语言:函数指针精讲

1、函数指针

一个函数总是占用一段连续的内存区域,函数名在表达式中有事也会被转换为该函数所在内存区域的首地址,这和数组名非常类似,我们可以把函数这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

函数指针的定义形式为:

returnType (*pointerName)(param list);

returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表。参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。

注意( )的优先级高于*,第一个括号不能省略,如果写作returnType *pointerName(param list);就成了函数原型它表明函数的返回值类型为 returnType *

实例:用指针来实现对函数的调用。

1. #include <stdio.h>
2.
3. //返回两个数中较大的一个
4. int max(int a, int b){
5. return a>b ? a : b;
6. }
7.
8. int main(){
9. int x, y, maxval;
10. //定义函数指针
11. int (*pmax)(int, int) = max; //也可以写作int (*pmax)(int a, int b)
12. printf("Input two numbers:");
13. scanf("%d %d", &x, &y);
14. maxval = (*pmax)(x, y);
15. printf("Max value: %d\\n", maxval);
16.
17. return 0; 
18. }

运行结果:

Input two numbers:10 50↙

Max value: 50

第 14 行代码对函数进行了调用。pmax 是一个函数指针,在前面加 * 就表示对它指向的函数进行调用。注意( )的优先级高于*,第一个括号不能省略。

1. int *p1[6]; //指针数组
2. int *(p2[6]); //指针数组,和上面的形式等价
3. int (*p3)[6]; //二维数组指针
4. int (*p4)(int, int); //函数指针

我相信大部分初学者对上面几种形式的指针都非常迷惑,不知道该从哪里入手去理解,为什么 p1、p2 是数组,而 p3 却是指针呢,它们仅仅是一个括号的区别。

复杂指针示例:

1. char *(* c[10])(int **p);
2. int (*(*(*pfunc)(int *))[5])(int *);

C 语言标准规定,对于一个符号的定义,编译器总是从它的名字开始读取,然后按照优先级顺序依次解析。对,从名字开始,不是从开头也不是从末尾,这是理解复杂指针的关键!

对于初学者,有几种运算符的优先级非常容易混淆,它们的优先级从高到低依次是:

  • 定义中被括号( )括起来的那部分。
  • 后缀操作符:括号( )表示这是一个函数,方括号[ ]表示这是一个数组。
  • 前缀操作符:星号*表示"指向 xxx 的指针"。

1) int p1[6];

从 p1 开始理解,它的左边是 *,右边是 [ ],[ ] 的优先级高于 *,所以编译器先解析 p1[6],p1 首先是一个拥有 6 个元素的数组,然后再解析 int *,它用来说明数组元素的类型。从整体上讲,p1 是一个拥有 6 个 int * 元素的数组,也即指针数组。

2) int (p3)[6];

从 p3 开始理解,( ) 的优先级最高,编译器先解析(*p3),p3 首先是一个指针,剩下的 int [6]是 p3 指向的数据的类型,它是一个拥有 6 个元素的一维数组。从整体上讲,p3 是一个指向拥有 6 个 int 元素数组的指针,也即二维数组指针。为了能够通过指针来遍历数组元素,在定义数组指针时需要进行降维处理,例如三维数组指针实际指向的数据类型是二维数组,二维数组指针实际指向的数据类型是一维数组,一维数组指针实际指向的是一个基本类型;在表达式中,数组名也会进行同样的转换(下降一维)。

3) int (p4)(int, int);

从 p4 开始理解,( ) 的优先级最高,编译器先解析(*p4),p4 首先是一个指针,它后边的 ( ) 说明 p4 指向的是一个函数,括号中的 int, int 是参数列表,开头的 int 用来说明函数的返回值类型。整体来看,p4 是一个指向原型为 int func(int, int);的函数的指针。

4) char ( c[10])(int p);

这个定义有两个名字,分别是 c 和 p,乍看起来 p 是指针变量的名字,不过很遗憾这是错误的。如果 p 是指针变量名,c[10]这种写法就又定义了一个新的名字,这让人匪夷所思。

以 c 作为变量的名字,先来看括号内部(绿色粗体):

[ ]的优先级高于 ,编辑器先解析c[10],c首先是一个数组,它前面的表明每个数组元素都是一个指针,只是还不知道指向什么类型的数据,整体上来看,(*c[10])说明c是一个指针数组,只是指针指向的数据类型尚未确定。

跳出括号,根据优先级规则(()的优先级高于*)应该先看右边(红色字体)

()说明是一个函数,int **p是函数参数。

再看左边(橘黄色粗体)

char*是函数的返回值类型

从整体上看,我们可以将定义分成两部分:

绿色粗体表明c是一个指针数组,红色粗体表明指针指向的数据类型,合起来就是:c是一个拥有10元素的指针数组,每个指针指向一个原型为char*fun(int **p)的函数。

5)从pfunc开始理解,先看括号内部(绿色粗体);

pfunc是一个指针。跳出括号,看他的两边(红色字体)

根据优先级规则应该先看右边的(int*),它表明这是一个函数,int是参数列表。再看左边的,它表明函数的返回值是一个指针,只是指针指向的数据类型尚未确定。

将上面的两部分合成一个整体,如下面的蓝色粗体所示,它表明pfunc是一个指向函数的指针,现在函数的参数列表确定了,也知道返回值是一个指针了(只是不知道它指向什么类型的数据)

蓝色粗体以外的部分,就是用来说明函数返回什么类型的指针。

我们接着分析,再向外跳一层括号(红色粗体):

[ ]的优先级高于*,先看右边,[5]表示这是一个数组,再看左边,表示数组的每个元素都是指针,也就是说,[5]是一个指针数组,函数返回的指针就指向这样一个数组。

那么,指针数组中的指针又指向什么类型的数据呢?再向外跳一层括号(橘黄色粗体):

先看橘黄色部分的右边,它是一个函数,再看左边,它是函数的返回值类型。也就是说,指针数组中的指针指向原型为int func(int*);的函数。

将上面的三部分合起来就是:pfunc是一个函数指针(蓝色部分),该函数的返回值是一个指针,它指向一个指针数组(红色部分),指针数组中的指针指向原型为int func(int*)的函数(橘黄色部分)

相关推荐
半桔11 分钟前
(C语言)文件操作
c语言·开发语言
向宇it26 分钟前
【unity小技巧】Unity 四叉树算法实现空间分割、物体存储并进行查询和碰撞检测
开发语言·算法·游戏·unity·游戏引擎
我真的太难了啊30 分钟前
学习QT第二天
开发语言·qt·学习
伏虎山真人33 分钟前
QT程序开机自启方案
开发语言·qt
lsx20240643 分钟前
Ruby 模块(Module)
开发语言
豆包MarsCode1 小时前
我用豆包MarsCode IDE 做了一个 CSS 权重小组件
开发语言·前端·javascript·css·ide·html
铅华尽1 小时前
Java---JDBC案例--手机信息管理系统
java·开发语言·智能手机
nuo5342021 小时前
The 2024 ICPC Kunming Invitational Contest
c语言·数据结构·c++·算法
X 西安1 小时前
第十章JavaScript的应用
开发语言·javascript·ecmascript
特种加菲猫1 小时前
初阶数据结构之队列的实现
开发语言·数据结构·笔记