7.3 函数指针的使用方法

在第1章讲解函数指针的时候,我们简要介绍了函数指针和指针函数的区别,同时也列举了几个简单的实例代码,接下来介绍函数指针在代码中的两种使用方法,一种是作为函数参数,而另外一种是用其调用函数。先来回顾一下函数指针的声明方法。


类型标识符(*指针变量名)(形参列表);


“类型标识符”是函数的返回类型,由于“()”的优先级高于“*”,因此指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表,例如:


int function(int x,int y);/声明一个函数/

int(f)(int x,int y);/声明一个函数指针*/

f=function;/将function函数的首地址赋给指针f/


赋值时,函数function不带括号,也不带参数,由于function代表函数的首地址,因此经过赋值以后,指针f就指向函数“function(int x,int y);”代码的首地址。

下面的程序说明了函数指针调用函数的方法。


include<stdio.h>

int max(int x,int y)

{

return x>y?x:y;

}

int min(int x,int y)

{

return x<y?x:y;

}

void main()

{

int(*f)(int x,int y)=max;

printf("max(2,6)=%d\tf(5,4)=%d\n",max(2,6),f(5,4));

f=min;

printf("min(2,6)=%d\tf(5,4)=%d\n",min(2,6),f(5,4));

return;

}


运行结果:


max(2,6)=6(f)(5,4)=5

min(2,6)=2(f)(5,4)=4


f是指向函数的指针变量,所以可把函数max赋给f作为f的值,即把max()的入口地址赋给f,以后就可以通过f来调用该函数。实际上,f和max都指向同一个入口地址,不同的是,f是一个指针变量,不像函数名称那样是固定的,它可以指向任何与它类型相同的函数。在程序中,把哪个函数的地址赋给f,f就指向哪个函数,而后用指针变量调用它,因此可以先后指向不同的函数。不过要注意,指向函数的指针变量没有++和—运算,使用时要小心。

接下来介绍另一种函数指针的声明方式,它可以分为以下两步。

定义函数指针类型:


typedef int(*fun_ptr)(int,int);


声明变量并赋值:


fun_ptr max_func=max;


需要注意的是,有的编译器并不支持省略参数,这要视情况而定。通过这种声明方式定义一个函数指针,其指向的是返回值为int型,同时带有两个int型参数的函数。使用该方式定义的函数指针名可以作为一种定义类型,通过它来定义一个变量,该变量就是声明的函数指针类型,但是要注意赋给函数指针的函数应该和函数指针所指的函数原型是一致的。具体的运用通过下面的一段代码来了解。


include<stdio.h>

void fun1()

{

printf("This is fun1\n");

return;

}

void fun2()

{

printf("This is fun2\n");

return;

}

void main()

{

typedef void(*funp)();

funp fun=fun1;

fun();

fun=fun2;

fun();

return;

}


运行结果:


This is fun1

This is fun2


从上面的代码可以看到,先采用“typedef void(*funp)();”定义一个函数指针,接下来用funp定义一个该函数指针类型的变量fun,因为函数指针不同于函数名,函数指针可以灵活地指向与它具有相同类型的函数,因此对于在上面的代码中定义的两个具有相同类型的函数fun1和fun2,都采用函数指针的方式来调用,得到了正确的结果。需要注意的是,在使用函数指针调用函数的时候,唯一发生变化的是函数名。

讲解完通过函数指针调用函数的使用,接下来介绍函数指针作为函数参数的使用。


include<stdio.h>

typedef int(*print)(int);

int fun1(int i)

{

return(int)i;

}

void fun2(int n,print prt)

{

for(int k=0;k<n;k++)

{

printf("%d\t",prt(k));

if(0==(k+1)%3)

printf("\n");

}

return;

}

void main()

{

int n=9;

fun2(n,fun1);

return;

}


运行结果:


0 1 2

3 4 5

6 7 8


从上面的代码可以看到,定义了一个函数指针,在fun2函数中定义了一个函数指针类型的参数,所以在接下来调用函数指针的时候,对相应的函数指针参数调用一个与其类型一致的函数,成功地实现了通过函数指针作为参数对函数进行调用的操作。

最后看函数名,为什么通过函数名对函数指针进行赋值操作就可以用函数指针成功地实现函数的调用呢?首先需要明白的一个概念是,函数名本身就是一个地址,它的特殊之处就在于它是一个函数的入口地址,并且该入口地址有其特殊的地方,看下面的代码。


include<stdio.h>

void print()

{

printf("Hello World\n");

return;

}

void main()

{

printf("&print=%d\n",&print);

printf("print=%d\n",print);

printf("print=%d\n",print);

void(*pfun)();

pfun=print;

printf("pfun=%d\n",pfun);

printf("pfun=%d\n",pfun);

printf("pfun=%d\n",pfun);

printf("&pfun=%d\n",&pfun);

pfun();

return;

}


运行结果:


&print=4198420

print=4198420

**print=4198420

*pfun=4198420

**pfun=4198420

pfun=4198420

&pfun=1245052

Hello World


从上面的运行结果可以发现,用函数名取地址得到的同样是函数的入口地址,而且在通过*号与函数名结合试图取出该地址的内容时发现,得到的仍然是该函数的入口地址,从而可以看出,函数名本身就是一个入口地址。接下来定义了一个函数指针,函数指针的特殊之处在于它有一个存储单元,用来存储函数的入口地址。但是当将函数指针指向一个函数的入口地址时,接下来对函数指针取值的操作得到的结果跟前面对函数名的操作所得结果完全一致。