5.11 指向函数的指针
在 C 语言中,函数本身不是变量,但可以定义指向函数的指针
这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等等
为了说明指向函数的指针的用法,我们接下来将修改本章前面的排序函数
在给定可选参数 -n
的情况下,该函数将按数值大小而非字典顺序对输入行进行排序
排序程序通常包括 3 部分:
- 判断任何两个对象之间次序的比较操作
- 颠倒对象次序的交换操作
- 一个用于比较和交换对象直到所有对象都按正确次序排列的排序算法
由于排序算法与比较、交换操作无关
因此,通过在排序算法中调用不同的比较和交换函数,便可以实现按照不同的标准排序
这就是我们的新版本排序函数所采用的方法
对比 Java 中的 Comparator 接口
可以看出,Java 多态的本质就是用接口或父类作为函数指针来编程
通过运行时动态传入的具体的指针来动态切换指向的函数实现
我们在前面讲过,函数 strcmp
按字典顺序比较两个输入行
在这里,我们还需要一个以数值为基础来比较两个输入行,并返回与 strcmp
同样的比较结果的函数 numcmp
这些函数在 main
之前声明,并且,指向恰当函数的指针将被传递给 qsort
函数。在这里,参数的
出错处理并不是问题的重点,我们将主要考虑指向函数的指针问题
#include <stdio.h>
#include <string.h>
#define MAXLINES 5000 /* max #lines to be sorted */
char *lineptr[MAXLINES]; /* pointers to text lines */
int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
/* sort input lines */
main(int argc, char *argv[])
{
int nlines; /* number of input lines read */
int numeric = 0; /* 1 if numeric sort */
if (argc > 1 && strcmp(argv[1], "-n") == 0)
numeric = 1;
if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
qsort((void**) lineptr, 0, nlines - 1, (int (*)(void*,void*))(numeric ? numcmp : strcmp));
writelines(lineptr, nlines);
return 0;
} else {
printf("input too big to sort\n");
return 1;
}
}
在调用函数 qsort
的语句中,strcmp
和 numcmp
是函数的地址
因为它们是函数,所以前面不需要加上取地址运算符 &
,同样的原因,数组名前面也不需要 &
运算符
改写后的 qsort
函数能够处理任何数据类型,而不仅仅限于字符串
从函数 qsort
的原型可以看出,它的参数表包括一个指针数组、两个整数和一个有两个指针参数的函数
其中,指针数组参数的类型为通用指针类型 void *
由于任何类型的指针都可以转换为 void *
类型,并且在将它转换回原来的类型时不会丢失信息
所以,调用 qsort
函数时可以将参数强制转换为 void *
类型
比较函数的参数也要执行这种类型的转换
这种转换通常不会影响到数据的实际表示,但要确保编译器不会报错
/* qsort: sort v[left]...v[right] into increasing order */
void qsort(void *v[], int left, int right, int (*comp)(void *, void *))
{
int i, last;
void swap(void *v[], int, int);
if (left >= right) /* do nothing if array contains */
return; /* fewer than two elements */
swap(v, left, (left + right) / 2);
last = left;
for (i = left + 1; i <= right; i++)
if ((*comp)(v[i], v[left]) < 0)
swap(v, ++last, i);
swap(v, left, last);
qsort(v, left, last-1, comp);
qsort(v, last+1, right, comp);
}
qsort
函数的第四个参数声明为 int (*comp)(void *, void *)
它表明 comp
是一个指向函数的指针,该函数具有两个 void *
类型的参数
其返回值类型为 int
语句 if ((*comp)(v[i], v[left]) < 0)
中的 comp
的使用和其声明是一致的
comp
是一个指向函数的指针,*comp
代表一个函数
语句 (*comp)(v[i], v[left])
对该函数进行调用
其中的圆括号是必须的,这样才能够保证其中的各个部分正确结合
如果没有括号,例如写成 int *comp(void *, void *) /* WRONG */
则表明 comp
是一个函数,该函数返回一个指向 int
类型的指针
我们在前面讲过函数 strcmp
,用于比较两个字符串
这里介绍的函数 numcmp
也是比较两个字符串
但它通过调用 atof
计算字符串对应的数值,然后在此基础上进行比较:
#include <stdlib.h>
/* numcmp: compare s1 and s2 numerically */
int numcmp(char *s1, char *s2)
{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1 < v2)
return -1;
else if (v1 > v2)
return 1;
else
return 0;
}
交换两个指针的 swap
函数和本章前面所述的 swap
函数相同
但它的参数声明为 void *
类型
void swap(void *v[], int i, int j;)
{
void *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}