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是一个指针数组,只是指针指向的数据类型尚未确定。 跳出括号,根据优先级规则(()的优先级高于\*)应该先看右边(红色字体) ![](https://i-blog.csdnimg.cn/direct/06e4528958ae4e2f8c39830f89e5ed6d.png) ()说明是一个函数,int \*\*p是函数参数。 再看左边(橘黄色粗体) ![](https://i-blog.csdnimg.cn/direct/6bb5c1f3fac341b88d49e6988c5ac7a1.png) char\*是函数的返回值类型 从整体上看,我们可以将定义分成两部分: ![](https://i-blog.csdnimg.cn/direct/6bcbfb3ccfa04436a829583428f4bce0.png) 绿色粗体表明c是一个指针数组,红色粗体表明指针指向的数据类型,合起来就是:c是一个拥有10元素的指针数组,每个指针指向一个原型为char\*fun(int \*\*p)的函数。 5)从pfunc开始理解,先看括号内部(绿色粗体); ![](https://i-blog.csdnimg.cn/direct/19bba29c21484bbea3dbf151ef2fabff.png) pfunc是一个指针。跳出括号,看他的两边(红色字体) ![](https://i-blog.csdnimg.cn/direct/93ed31b446c14e379a0b8958bb4680bd.png) 根据优先级规则应该先看右边的(int\*),它表明这是一个函数,int*是参数列表。再看左边的*,它表明函数的返回值是一个指针,只是指针指向的数据类型尚未确定。 将上面的两部分合成一个整体,如下面的蓝色粗体所示,它表明pfunc是一个指向函数的指针,现在函数的参数列表确定了,也知道返回值是一个指针了(只是不知道它指向什么类型的数据) ![](https://i-blog.csdnimg.cn/direct/59504d15257948138a733c757c454369.png) 蓝色粗体以外的部分,就是用来说明函数返回什么类型的指针。 我们接着分析,再向外跳一层括号(红色粗体): ![](https://i-blog.csdnimg.cn/direct/d377e656263644a9917116597a87f229.png) \[ \]的优先级高于\*,先看右边,\[5\]表示这是一个数组,再看左边,*表示数组的每个元素都是指针,也就是说,*\[5\]是一个指针数组,函数返回的指针就指向这样一个数组。 那么,指针数组中的指针又指向什么类型的数据呢?再向外跳一层括号(橘黄色粗体): ![](https://i-blog.csdnimg.cn/direct/92efd75709454925ac75a315a06e2a48.png) 先看橘黄色部分的右边,它是一个函数,再看左边,它是函数的返回值类型。也就是说,指针数组中的指针指向原型为int func(int\*);的函数。 将上面的三部分合起来就是:pfunc是一个函数指针(蓝色部分),该函数的返回值是一个指针,它指向一个指针数组(红色部分),指针数组中的指针指向原型为int func(int\*)的函数(橘黄色部分)

相关推荐
秋风&萧瑟4 分钟前
【QT】QT的多界面跳转以及界面之间传递参数
开发语言·qt
骑牛小道士7 分钟前
JAVA- 锁机制介绍 进程锁
java·开发语言
郭涤生10 分钟前
Chapter 1: Historical Context_《C++20Get the details》_notes
开发语言·c++20
独好紫罗兰21 分钟前
洛谷题单2-P5712 【深基3.例4】Apples-python-流程图重构
开发语言·python·算法
东方佑36 分钟前
深度解析Python-PPTX库:逐层解析PPT内容与实战技巧
开发语言·python·powerpoint
水w1 小时前
【Android Studio】如何卸载干净(详细步骤)
android·开发语言·android studio·activity
weixin_307779131 小时前
判断HiveQL语句为建表语句的识别函数
开发语言·数据仓库·hive·c#
一顿操作猛如虎,啥也不是!1 小时前
JAVA-Spring Boot多线程
开发语言·python
v维焓1 小时前
C++(思维导图更新)
开发语言·c++·算法
jiet_h1 小时前
深入解析KSP(Kotlin Symbol Processing):现代Android开发的新利器
android·开发语言·kotlin