函数指针和指针函数是两个很容易混淆的概念,可以这样增直观理解:函数指针,是一个指针,一个指向函数基地址的指针,由此指针可以进行函数调用;指针函数是一个函数,一个返回指针值的函数。
1 指针函数
返回指针类型的函数简称指针函数。如用“TypeName"表示某一个数据类型,则“TypeName *pValue”表示定义一个返回TypeName类型的指针函数和一个指针变量pValue,而且这个指针变量必须正确初始化。假设这个指针变量不是通过参数表传递,而是在函数中产生,则可以表示为:
TypeName * FunName(参数表)
{
TypeName * pValue;
//函数体包括正确初始化指针变量pValue
//函数体
return pValue;
}
一个简单实例的代码:
//返回结构及结构指针的例子
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
//定义一个全局的结构体
struct LIST{
int a,b;
}d[3],*p;
//返回结构值
struct LIST make1(int x, int y)
{
struct LIST temp;
temp.a=x;
temp.b=y;
return temp;
}
//返回结构指针
struct LIST *make2(int x, int y)
{
struct LIST *temp;
temp=(struct LIST *)malloc(sizeof(struct LIST));
temp->a=x;
temp->b=y;
return temp;
}
int main()
{
int i;
d[0]=make1(215, 512);
p=make2(815,518);
d[1]=*p;
d[2]=d[0]; //结构可以整体赋值
for(i=0;i<3;i++)
printf("d[%d].a=%d,d[%d].b=%d\n", i,d[i].a,i,d[i].b);
system("pause");
return 0;
}
运行结果:
d[0].a=215,d[0].b=512
d[1].a=815,d[1].b=518
d[2].a=215,d[2].b=512
假设函数的参数表中有“TypeN *”类型的参数value,则可以表示为如下形式:
TypeName FunName(TypeName *value, 其他参数表)
{
//函数体
return value;
}
一个简单实例:
#include <stdlib.h>
//将结构指针作为返回值的例子。
#include <stdio.h>
struct LIST{
int a,b;
}d[3], b, *p;
struct LIST make(int , int); //返回结构的函数
struct LIST *change (struct LIST*, struct LIST [ ]); //返回结构的指针
void disp(struct LIST [ ]); //使用结构参数
int main()
{
d[0]=make(215,512);
d[1]=make(815,518);
d[2]=make(618,816);
p=&b; //初始化指针p
p=change(p,d); //表达式调用
disp(d);
printf("b.a=%d\t\tb.b=%d\n", b.a, b.b);
printf("p->a=%d\t\tp->b=%d\n", p->a, p->b);
system("pause");
return 0;
}
struct LIST make(int x, int y)
{
struct LIST temp;
temp.a=x;
temp.b=y;
return temp;
}
void disp(struct LIST d[ ])
{
int i;
for(i=0;i<3;i++)
printf("d[%d].a=%d\td[%d].b=%d\n", i,d[i].a,i,d[i].b);
}
//将传递的一个结构参数作为函数的返回值
struct LIST* change(struct LIST *b,struct LIST d[ ])
{
b->a=d[0].a;
b->b=d[2].b;
return b;
}
运行结果:
d[0].a=215 d[0].b=512
d[1].a=815 d[1].b=518
d[2].a=618 d[2].b=816
b.a=215 b.b=816
p->a=215 p->b=816
2 函数指针
指针变量可以指向整型变量、字符变量及字符串、浮点变量和数组。其实,指针变量也可以指向一个函数。因为尽管函数本身不是一个变量,但它在内存中仍然有其物理地址。在编译过程中,原代码被转换成目标代码,函数的入口地址也同时确立,所以就能够将函数的入口地址赋给指针变量。程序调用函数,也就是机器语言的"CALL"指向了这个入口点。因为指向函数的指针实际上包含了函数的入口点的内存地址,所以赋给指针变量的地址就是函数的入口地址,从而该指针就可以用来代替函数名。这一特性也使得函数可以作为实参传递给其他函数。
通过函数指针变量可以完成对函数的调用。其原理是通过把一个函数(函数名)赋给一个函数指针变量,然后又通过该函数指针变量来完成对函数的引用。
一个小实例:
使用函数指针输出多项式x3-4x+6和x2-3x在区间[-1,1]增长步长为0.1时的所有结果。
#include <stdlib.h>
#include <stdio.h>
double f1(double x);
double f2(double x);
//double (*p)(double);
#define STEP 0.1
int main( )
{
int i;
double x;
double (*p)(double); //double (*p)(double)与double (*p)( )等效
for ( i=0; i<2; i++)
{
printf("第%d个方程:\n",i+1);
if ( i==0)
p = f1;
//p = f1(x);
else
p = f2;
for( x = -1; x <= 1; x += STEP)
printf("%f\t%f\n", x, (*p)(x));
}
system("pause");
return 0;
}
//函数 f1
double f1(double x)
{ return ( x*x*x - 5*x -4);}
// 函数f2
double f2(double x)
{return( x*x - 3*x +2);}
运行结果:
第1个方程:
-1.000000 0.000000
-0.900000 -0.229000
-0.800000 -0.512000
-0.700000 -0.843000
-0.600000 -1.216000
-0.500000 -1.625000
-0.400000 -2.064000
-0.300000 -2.527000
-0.200000 -3.008000
-0.100000 -3.501000
-0.000000 -4.000000
0.100000 -4.499000
0.200000 -4.992000
0.300000 -5.473000
0.400000 -5.936000
0.500000 -6.375000
0.600000 -6.784000
0.700000 -7.157000
0.800000 -7.488000
0.900000 -7.771000
1.000000 -8.000000
第2个方程:
-1.000000 6.000000
-0.900000 5.510000
-0.800000 5.040000
-0.700000 4.590000
-0.600000 4.160000
-0.500000 3.750000
-0.400000 3.360000
-0.300000 2.990000
-0.200000 2.640000
-0.100000 2.310000
-0.000000 2.000000
0.100000 1.710000
0.200000 1.440000
0.300000 1.190000
0.400000 0.960000
0.500000 0.750000
0.600000 0.560000
0.700000 0.390000
0.800000 0.240000
0.900000 0.110000
1.000000 0.000000
语句double (*p)( );仅仅说明p是一个指向函数的指针变量,此函数带回实型的返回值。但这并不是固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,专门用来存储函数的入口地址。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。
函数指针声明相对于函数声明,多了“(*)”符号,即多了一对圆括号和一个星号。
如果再增加一对中括号和一个数字,如double (*p[3])( );则成了一个函数指针数组变量,也就是拥有了三个函数指针,分别是p[0]、p[1]、p[2]。
函数指针还可以用作函数参数或函数返回值,具体内容请见:C|函数指针做为函数参数或函数返回值。
-End-