定义一个函数指针
int (*f)();
定义一个函数指针数组
int *(*f[]) ();
函数指针最常见的用途是:1、转换表(jump table); 2、作为参数传递给另一个函数
简单声明一个函数指针并不意味着它可以马上使用,必须对它进行初始化:
int f(int);
int (*pf) (int) = &f; //创建函数指针pf,并且把它初始化为指向函数f
在函数指针被声明并且初始化之后,我们就可以使用三种方式调用函数:
int ans;
ans = f(25);
ans = (*pf)(25);
ans = pf(25);
第一种方式函数名f首先被转换成为一个函数指针,该指针指定函数在内存中的位置。然后,函数调用操作符调用该函数,执行开始于这个地址的代码。
第二种方式对pf进行间接访问操作,它把函数指针转换成为一个函数名。然后与第一种方式完全相同。
第三种方式显示了函数指针通常是如何使用的。
回调函数
写一个函数,实现类型无关的链表查找。
#include<stdio.h>
#include"node.h"
Node * search_list( Node * node, void const *value, int (*compare)(void const *, void const *))
{
while( node != NULL){
if( compare( &node->value, value) == 0 )
break;
node = node->link;
}
return node;
}
如何写比较函数:
int compare_ints( void const *a, void const *b)
{
if( *(int *)a == *(int *)b ) //注意强制类型转换,比较函数的参数必须声明为void *以匹配查找函数的原型,然后它们再强制转换为int *类型,用于比较整形值。
return 0;
else
return 1;
}
如何使用这个函数:
desired_node = search_list( root, &desired_value, compare_ints );
转移表(jump table)
转移表最好用个例子来解释。下面的代码段取自于一个程序,它用于实现一个袖珍计算器。程序的其他部分已经读入两个数(op1 和 op2)和一个操作符(oper)。下面的代码对操作符进行测试,然后决定调用哪个函数。
switch(oper){
case ADD:
result = add(op1, op2);
break;
case SUB:
result = sub(op1, op2);
break;
case MUL:
result = mul(op1, op2);
break;
case DIV:
result = div(op1, op2);
break;
}
为了使用switch语句,表示操作符的代码必须是整数。如果它们是从零开始连续的整数,我们可以使用转换表来实现相同的任务。转换表就是一个函数指针的数组。
创建一个转换表需要两个步骤。首先,声明并初始化一个函数指针数组。唯一需要留心之处就是确保这些函数的原型出现在这个数组的声明之前。
double add( double, double );
double sub( double, double );
double mul( double, double );
double div( double,double );
...
double (*oper_func[ ]) ( double, double ) ={ add, sub, mul, div, ...};
初始化列表中各个函数名的正确顺序取决于程序中用于表示每个操作符的整形代码。这个例子假定ADD是0,SUB是1,MUL是2,接下去以此类推。
第二个步骤是使用下面这条语句替换前面整条switch语句。
result = oper_func[ oper ]( op1,op2 ); //oper从数组中选择正确的函数指针,而函数调用操作符将执行这个函数
字符串常量
当一个字符串常量出现在表达式中时,它的值是个指针常量。编译器把这些指定字符的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针。但是,当数组名用于表达式中时,它们的值也是指针常量。我们可以对它们进行下标引用、间接访问以及指针运算。
"xyz"+1; //字符串常量实际上是个指针,这个表示“指针值上加1”,结果是个指针,指向字符串中的第2个字符:y
*“xyz”; //字符串常量类型是“指向字符的指针”,所以这个间接访问的结果就是它所指向的字符:x