1、函数指针(指向函数的指针)
一个函数在编译之后,会占据一部分内存,而它的函数名,就是这段函数的首地址。
可以把一个指针声明成为一个指向函数的指针。
C 语言规定函数名会被转换为指向这个函数的指针,除非这个函数名作为 & 操作符或 sizeof 操作符的操作数(注意:函数名用于 sizeof 的操作数是非法的)。也就是说 f = test; 中 test 被自动转换为 &test,而 f = &test; 中已经显示使用了 &test,所以 test 就不会再发生转换了。因此直接引用函数名等效于在函数名上应用 & 运算符,两种方法都会得到指向该函数的指针。
指向函数的指针必须初始化,或者具有 0 值,才能在函数调用中使用。
与数组一样:
(1)禁止对指向函数的指针进行自增运算++
(2)禁止对函数名赋值,函数名也不能用于进行算术运算。
示例1:
int fun1(int,int);
int fun1(int a, int b){
return a+b;
}
int main(){
int (*pfun1)(int,int);
pfun1=fun1;//这里&fun1和fun1的值和类型都一样,用哪个无所谓
int a=(*pfun1)(5,7); //通过函数指针调用函数。
}
示例2:
#include <stdio.h>
#include <stdlib.h>
int Max(int x, int y) //定义Max函数
{
int z;
if (x > y) {
z = x;
}else {
z = y;
}
return z;
}
int main() {//定义一个函数指针
int(*p)(int, int);
int a, b, c;//把函数Max赋给指针变量p, 使p指向Max函数
p = Max;
printf("please enter a and b:");
scanf("%d%d", &a, &b);//通过函数指针调用Max函数
c = (*p)(a, b);
printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
system("pause");
return 0;
}
```
示例2:
```c
#include <stdio.h>
void test( ){
printf("test called!/n");
}
int main( ){
void (*f) ( );
f = test;
f ( );
(*f)( );
//test++; // error,标准禁止对指向函数的指针进行自增运算
//test = test + 2; // error,不能对函数名赋值,函数名也不能用于进行算术运算
printf("%p/n", test);
printf("%p/n", &test);
printf("%p/n", *test);
return 0;
}
运行结果为:
test called!
test called!
004013EE004013EE004013EE
这里的玄学就是 *test 为什么能和上面两个之前介绍过的输出一样的值。
首先来看函数名 test,是一个符号用来标识一个函数的入口地址,在使用中函数名会被转换为指向这个函数的指针,指针的值就是函数的入口地址,&test 在前面已经说了:显示获取函数的地址。*test 可以认为由于 test 已经被转换成了函数指针, 指向这个函数,所以 *test 就是取这个指针所指向的函数名,而又根据函数名会被转换指向该函数的指针的规则,这个函数也转变成了一个指针,所以 *test 最终也是一个指向函数 test 的指针。也就是说:*test --> *(&test) --> test --> &test。
上述关系十分重要!
为了更加明确,把示例 1 做补充:
#include <stdio.h>
int fun1(int,int);
int fun1(int a, int b){
return a+b;
}
/* 要调用上面定义函数的主函数 */
int main (){
int (*pfun1)(int,int);
pfun1=fun1;//这里&fun1和fun1的值和类型都一样,用哪个无所谓
int a=(*pfun1)(5,7); //通过函数指针调用函数。
printf("%d\n",a);
int e = fun1(5,7);
printf("%d\n",d)
int b = (&fun1)(5,7);
printf("%d\n",b);
int c = (*fun1)(5,7);
printf("%d",c);
return 0;
}
//根据关系 fun1==&fun1fun1&fun1 可知,以上的运行结果会得到4个5+7。
//因此在下面的函数指针数组实例中,action2就相当于这里的(&fun1(5,7)),这点务必搞清楚。
2、指针函数(返回值为指针的函数)
所谓指针函数,就是返回指针的函数。在前面笔记中“从函数返回数组”中已经介绍。
C 语言的库函数中有很多都是指针函数,比如字符串处理函数,下面给出一些函数原型:
char *strcat( char *dest, const char *src );
char *strcpy( char *dest, const char *src );
char *strchr( const char *s, int c );
char *strstr( const char *src, const char *sub );
3、两者混用(不常用)
注意函数的返回值不仅仅局限于指向变量的指针,也可以是指向函数的指针。
首先来看这个声明:int (function(int)) (double, char); 要了解此声明的含义,首先来看 function(int),将 function 声明为一个函数,它带有一个 int 型的形式参数,这个函数的返回值为一个指针,正是函数指针 int () (double*, char); 这个指针指向一个函数,此函数返回 int 型并带有两个分别是 double* 型和 char 型的形参。
如果使用typedef可以将这个声明简化:(没看懂。。。。之后的结构体再补充)
typedef int (*ptf) (double*, char);
ptf function( int );
另一个例子:
void (*signal (int sig, void (*func) (int siga)) ) ( int siga );
现在要分析的是 signal,因为紧邻 signal 的是优先级最高的括号,首先与括号结合,所以 signal 为一个函数,括号内为 signal 的两个形参,一个为int型,一个为指向函数的指针。接下来从向左看,* 表示指向某对象的指针,它所处的位置表明它是 signal 的返回值类型,现在可以把已经分析过的 signal 整体去掉,得到 void (*) ( int siga )。又是一个函数指针,这个指针与 signal 形参表中的第二个参数类型一样,都是指向接受一个 int 型形参且不返回任何值的函数的指针。
用 typedef 可以将这个声明简化:
typedef int (*p_sig) (double*, char);
p_sig signal(int sig, p_sig func);
这个 signal 函数是 C 语言的库函数,在 signal.h 中定义,用来处理系统中产生的信号。
4、函数指针数组
假设现在有一个文件处理程序,通过一个菜单按钮来选择相应的操作(打开文件,读文件,写文件,关闭文件)。这些操作都实现为函数且类型相同,分别为:
void open();
void read();
void write();
void close();
现在定义一个函数指针类型的别名PF:
typedef void (*PF) ( );
把以上 4 种操作取地址放入一个数组中,得到:
PF file_options[ ] = {
&open,
&read,
&write,
&close
};
如果不使用 typedef,那么分析起来就会比较复杂,结果是 void (*file_options[ ]) ( );
这个数组中的元素都是指向不接受参数且不返回任何值的函数的指针,因此这是一个函数指针数组。接下来,定义一个函数指针类型的指针action并初始化为函数指针数组的第一个元素:PF* action = file_options;,如果不好理解,可以类比一下:
int ia[4] = {0, 1, 2, 3};
int *ip = ia;,
这里 PF 相当于 int,这样应该比较好懂了。
复习:
int ia[4] = {0, 1, 2, 3};
int *ip = ia; //ia就是&ia[0],因此ip指向ia[0]。与此同时ip[1]的含义又和*(ip+1)一样。
printf("%p\n",ip);
printf("%p\n",ip+1);
printf("%d\n",ip[1]);
printf("%d\n",*(ip+1));/*输出结果0x7ffee4cca9b00x7ffee4cca9b411*/
通过对指针 action 进行下标操作可以调用数组中的任一操作,如:action2 会调用 write 操作,以此类推。在实际中,指针 action 可以和鼠标或者其他 GUI 对象相关联,以达到相应的目的。
5、函数与指针的复杂声明(不做要求,一般用 typedef 代替它)
只举一个例子:
int *(*(*fp)(int)) [10];
阅读步骤:
1.从未定义的变量名开始阅读 -------------------------------------------- fp
2.往右看,什么也没有,遇到了),因此往左看,遇到一个* ------ 一个指向某对象的指针
3.跳出括号,遇到了(int) ----------------------------------- 一个带一个int参数的函数
4.向左看,发现一个* --------------------------------------- (函数)返回一个指向某对象的指针
5.跳出括号,向右看,遇到[10] ------------------------------ 一个10元素的数组
6.向左看,发现一个* --------------------------------------- 一个指向某对象指针
7.向左看,发现int ----------------------------------------- int类型
所以 fp 是指向函数的指针(函数指针), 该函数返回一个指向数组的指针,此数组有 10 个 int* 型的元素。
int *(*(*fp)(int)) [10]; (*fp)(int)是一个指向函数的指针ptr*(*fp)(int)相当于一个指针ptr1(指针函数的返回值)最后剩下int *ptr1[10],可以理解。
# 欢迎使用Markdown编辑器