目录
【问题引入】
函数指针是什么?
一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。
简而言之,函数指针就是利用一个指针变量来指向函数,利用该指针变量来调用函数。
函数指针的定义形式:
returnType (*pointerName)(param list);
(returnType: 函数返回类型; pointerName:指针变量名; param list: 形参表)
注意( )
的优先级高于*
,第一个括号不能省略,如果写作returnType *pointerName(param list);
就成了函数原型,它表明函数的返回值类型为returnType *
。 即如果省略()会导致函数的返回类型变成指针型。
使用函数指针时,首先要对其进行定义,例如下面的式子:
【例1】
#include <stdio.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int main(void)
{
int a, b;
scanf("%d %d", &a, &b);
int (*pmax)(int a, int b) = max;
//也可以写成
//int (*pmax)(int , int) = max;
//类似于函数原形,可以省略参数名称
printf("%d", (*pmax)(a, b));
return 0;
}
这样我们就可以直接利用指针变量来调用函数。
接下来我们来看看这段代码,注意其中的区别:
【例2】
#include <stdio.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int main(void)
{
int a, b;
scanf("%d %d", &a, &b);
int (*pmax)(int a, int b) = max;
//也可以写成
//int (*pmax)(int , int) = max;
//类似于函数原形,可以省略参数名称
printf("%d\n", pmax(a, b));
int (*p)(int a, int b) = max;
int c;
c = p(a, b);
printf("%d\n", c);
return 0;
}
【问题1】
我们发现第一个例子中使用:
printf(“%d\n”, (*pmax)(a, b));
而第二个例子中使用:
printf("%d\n", pmax(a , b));
其产生的结果没有任何差异(接下来我们会讲原因)。同理第二个例子中定义的指针变量p也是同样的道理,那么就有:
p(a, b) 等价于 max(a, b)
【由函数指针引起的混乱】
这里不得不重新说明一下函数,我们知道当我们创建一个函数时,会有一个函数名,这个函数名实际上是一个指针变量,指向函数体,函数体存储在代码段(Text Segment)中,指针变量存储在栈中。即创建一个函数实际上就创建了一个指向函数体的指针变量。
那么当我们创建一个函数指针时,理应存在这样一个示意图:
这里可能会有同学要问这样一个问题:
【问题2】
对于一个函数: int max(int a,int b)
利用指针函数,需定义为: int (*pmax)(int a, int b)
这里会产生一个疑惑: pmax == max,为什么需要写成(*pmax)而不是写成 pmax
我会在下面给出解释。(这个问题和上一个问题类似)
在C语言中,表达式中的函数可以被解读成“指向函数的指针”,如果基于C语言函数的声明规则,int func() 这样的声明会被解释为“返回int类型的函数”,如果函数位于表达式中,只是取出func解释成“指向函数的指针”,这样使用会不会感到很怪异 ?
【理解1】
这里就产生了对于函数指针使用的两种不同的特殊情况:
- 位于声明中,则必须写成 returnType (*pointerName)(param list);
- 位于表达式中,则可以写成 pointerName(param list); 或者 * pointerName(param list);的形式。
是不是感觉C语言关于指向函数的指针的语法比较奇怪?
混乱产生的原因就是:“表达式中的函数可以解读为‘指向函数的指针’这个意图不明的规则(为了与数组保持一致)”
【理解2】
为了照顾到这种混乱,ANSI标准对语法做了一下例外的规定:
- 表达式中的函数自动转换成“指向函数的指针”。但是,当函数是地址运算符&或者sizeof运算符的操作数时,表达式中的函数不能变换成“指向函数的指针”。
- 函数调用运算符()的操作数不是“函数”,而是“函数的指针”。
那么为什么【问题1】中可以使用 *pmax(a,b)呢?
这里简单解释一下。如果对“指向函数的指针”使用解引用*,它会暂时成为函数,但是因为在表达式中,所以会被瞬间地变回成“指向函数的指针”。 即使对“指向函数的指针”使用*运算符,也是对牛弹琴,因此此时的运算符*发挥不了任何作用。
因此,以下句子也是可以正常执行:
(********printf)("Hello World!\n");
printf是标准输出函数,这里 * 什么作用都没有。
那么以上问题就已经解决了。
【特殊引用】
由于数组和指针的一致性,我们甚至可以这样来引用。
【我的问题】
上面我们指出函数名是一个指针变量,这个指针变量指向函数体,那么为什么会出现这样的输出结果呢?
希望有大佬在评论区发表自己的意见,这个问题我不理解,求助!!!!
#include <stdio.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int main(void)
{
int a, b;
scanf("%d %d", &a, &b);
int (*pmax)(int a, int b) = max;
printf("%p\n", max);
printf("%p\n", &max);
printf("%p\n", pmax);
printf("%p\n", &pmax);
return 0;
}
为什么 max 和 &max的结果一样呢?难道 max 不应该是一个指针变量嘛?
经过我一晚上的胡思乱想,我有了一个不太成熟的想法,来解释这个问题:
当创建一个数组的时候,会为其分配一段连续的存储空间,同时利用一个数组名来指向该数组的首地址,我们把这个数组名称为数组变量。
需要注意的的是,内存并不会为数组变量分配存储空间,这也是为什么数组名不能指向其他地方,而指针变量可以指向其他地方,因为内存为指针变量分配了存储空间。那么我们是不是可以认为,函数名是一种特殊的指针变量,内存同样不会对其分配存储空间,这也是为什么函数名不能指向其他地方。
我们发现这两者存在极大的相似之处,所以我提出了这样一个想法,函数变量(函数名)与数组变量(数组名)相似,在进行编译的时候,系统会把函数名和数组名出现的地方直接替换成函数或者数组的首地址!!!那么这里是不是又是一个特殊的发现?进行替换?是否与宏定义相关?
所以函数变量和数组变量是不是一种特殊的宏定义?????
希望有大神指正!!!