编辑 维基 ^ |< << 幻灯片65/221 >> >| |
5.11 Pointers to Functions 指向函数的指针
In C, a function itself is not a variable, but it is possible to define pointers to functions, which can be assigned, placed in arrays, passed to functions, returned by functions, and so on. We will illustrate this by modifying the sorting procedure written earlier in this chapter so that if the optional argument -n is given, it will sort the input lines numerically instead of lexicographically.
在C语言中,函数本身不是变量,但可以定义指向函数的指针。这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等等。为了说明指向函数的指针的用法,我们接下来将修改本章前面的排序函数,在给定可选参数-n的情况下,该函数将按数值大小而非字典顺序对输入行进行排序。
A sort often consists of three parts - a comparison that determines the ordering of any pair of objects, an exchange that reverses their order, and a sorting algorithm that makes comparisons and exchanges until the objects are in order. The sorting algorithm is independent of the comparison and exchange operations, so by passing different comparison and exchange functions to it, we can arrange to sort by different criteria. This is the approach taken in our new sort.
排序程序通常包括3部分:判断任何两个对象之间次序的比较操作、颠倒对象次序的交换操作、一个用于比较和交换对象直到所有对象都按正确次序排列的排序算法。出于排序算法与比较、交换操作无关,因此,通过在排序算法中调用不同的比较和交换函数,便可以实现按照不同的标准排序。这就是我们的新版本排序函数所采用的方法。
Lexicographic comparison of two lines is done by strcmp, as before; we will also need a routine numcmp that compares two lines on the basis of numeric value and returns the same kind of condition indication as strcmp does. These functions are declared ahead of main and a pointer to the appropriate one is passed to qsort. We have skimped on error processing for arguments, so as to concentrate on the main issues.
1 #include <stdio.h>
2 #include <string.h>
3
4 #define MAXLINES 5000 /* max #lines to be sorted */
5 char *lineptr[MAXLINES]; /* pointers to text lines */
6
7 int readlines(char *lineptr[], int nlines);
8 void writelines(char *lineptr[], int nlines);
9
10 void qsort(void *lineptr[], int left, int right,
11 int (*comp)(void *, void *));
12 int numcmp(char *, char *);
13
14 /* sort input lines */
15 main(int argc, char *argv[])
16 {
17 int nlines; /* number of input lines read */
18 int numeric = 0; /* 1 if numeric sort */
19
20 if (argc > 1 && strcmp(argv[1], "-n") == 0)
21 numeric = 1;
22 if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
23 qsort((void**) lineptr, 0, nlines-1,
24 (int (*)(void*,void*))(numeric ? numcmp : strcmp));
25 writelines(lineptr, nlines);
26 return 0;
27 } else {
28 printf("input too big to sort\n");
29 return 1;
30 }
31 }
我们在前面讲过,函数strcmp按字典顺序比较两个输入行。在这里,我们还需要一个以数值为基础来比较两个输入行。并返回与strcmp同样的比较结果的函数numcmp。这些函数在main之前声明,并且,指向恰当函数的指针将被传递给qsort函数。在这里,参数的出错处理并不是问题的重点,我们将主要考虑指向函数的指针问题。
1 #include <stdio.h>
2 #include <string.h>
3
4 #define MAXLINES 5000 /* max #lines to be sorted */
5 char *lineptr[MAXLINES]; /* pointers to text lines */
6
7 int readlines(char *lineptr[], int nlines);
8 void writelines(char *lineptr[], int nlines);
9
10 void qsort(void *lineptr[], int left, int right,
11 int (*comp)(void *, void *));
12 int numcmp(char *, char *);
13
14 /* sort input lines */
15 main(int argc, char *argv[])
16 {
17 int nlines; /* number of input lines read */
18 int numeric = 0; /* 1 if numeric sort */
19
20 if (argc > 1 && strcmp(argv[1], "-n") == 0)
21 numeric = 1;
22 if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
23 qsort((void**) lineptr, 0, nlines-1,
24 (int (*)(void*,void*))(numeric ? numcmp : strcmp));
25 writelines(lineptr, nlines);
26 return 0;
27 } else {
28 printf("input too big to sort\n");
29 return 1;
30 }
31 }
In the call to qsort, strcmp and numcmp are addresses of functions. Since they are known to be functions, the & is not necessary, in the same way that it is not needed before an array name.
在调用函数qsort的语句中,strcmp和numcmp是函数的地址。因为它们是函数,所以前面不需要加上取地址运算符&,同样的原因,数组名前面也不需要&运算符。
We have written qsort so it can process any data type, not just character strings. As indicated by the function prototype, qsort expects an array of pointers, two integers, and a function with two pointer arguments. The generic pointer type void * is used for the pointer arguments. Any pointer can be cast to void * and back again without loss of information, so we can call qsort by casting arguments to void *. The elaborate cast of the function argument casts the arguments of the comparison function. These will generally have no effect on actual representation, but assure the compiler that all is well.
1 /* qsort: sort v[left]...v[right] into increasing order */
2 void qsort(void *v[], int left, int right,
3 int (*comp)(void *, void *))
4 {
5 int i, last;
6
7 void swap(void *v[], int, int);
8
9 if (left >= right) /* do nothing if array contains */
10 return; /* fewer than two elements */
11 swap(v, left, (left + right)/2);
12 last = left;
13 for (i = left+1; i <= right; i++)
14 if ((*comp)(v[i], v[left]) < 0)
15 swap(v, ++last, i);
16 swap(v, left, last);
17 qsort(v, left, last-1, comp);
18 qsort(v, last+1, right, comp);
19 }
改写后的qsort函数能够处理任何数据类型,而不仅仅限于字符串。从函数qsort的原型可以看出,它的参数表包括一个指针数组、两个整数和一个有两个指针参数的函数。其中,指针数组参数的类型为通用指针类型vold *。由于任何类型的指针都可以转换为void *类型,并且在将它转换回原来的类型时不会丢失信息,所以,调用qsort函数时可以将参数强制转换为vold *类型。比较函数的参数也要执行这种类型的转换。这种转换通常不会影响到数据的实际表示,但要确保编译器不会报错。
1 /* qsort: sort v[left]...v[right] into increasing order */
2 void qsort(void *v[], int left, int right,
3 int (*comp)(void *, void *))
4 {
5 int i, last;
6
7 void swap(void *v[], int, int);
8
9 if (left >= right) /* do nothing if array contains */
10 return; /* fewer than two elements */
11 swap(v, left, (left + right)/2);
12 last = left;
13 for (i = left+1; i <= right; i++)
14 if ((*comp)(v[i], v[left]) < 0)
15 swap(v, ++last, i);
16 swap(v, left, last);
17 qsort(v, left, last-1, comp);
18 qsort(v, last+1, right, comp);
19 }
The declarations should be studied with some care. The fourth parameter of qsort is
1 int (*comp)(void *, void *)
which says that comp is a pointer to a function that has two void * arguments and returns an int.
我们仔细研究一下其中的声明。qsort函数的第四个参数声明如下:
int (*comp)(void *, void *)
它表明comp是一个指向函数的指针,该函数具有两个void *类型的参数,其返回值类型为int。
The use of comp in the line
1 if ((*comp)(v[i], v[left]) < 0)
is consistent with the declaration: comp is a pointer to a function, *comp is the function, and
1 (*comp)(v[i], v[left])
is the call to it. The parentheses are needed so the components are correctly associated; without them,
1 int *comp(void *, void *) /* WRONG */
says that comp is a function returning a pointer to an int, which is very different.
在下列语句中:
1 if ((*comp)(v[i], v[left]) < 0)
comp的使用和其声明是一致的,comp是一个指向函数的指针,*comp代表一个函数。下列语句是对该函数进行调用:
1 (*comp)(v[i], v[left])
其中的圆括号是必须的,这样才能够保证其中的各个部分正确结合。如果没有括号,例如写成下面的形式:
1 int *comp(void *, void *) /* WRONG */
则表明comp是一个函数,该函数返回一个指向int类型的指针,这同我们的本意显然有很大的差别。
We have already shown strcmp, which compares two strings. Here is numcmp, which compares two strings on a leading numeric value, computed by calling atof:
我们在前面讲过函数strcmp,它用于比较两个字符串。这里介绍的函数numcmp也是比较两个字符串,但它通过调用atof计算字符串对应的数值,然后在此基础上进行比较:
The swap function, which exchanges two pointers, is identical to what we presented earlier in the chapter, except that the declarations are changed to void *.
交换两个指针的swap函数和本章前面所述的swap函数相同,但它的参数声明为void *类型。
A variety of other options can be added to the sorting program; some make challenging exercises.
还可以将其他一些选项增加到排序程序中,有些可以作为较难的练习。
Exercise 5-14. Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order. Be sure that -r works with -n.
练习5-14 修改排序程序,使它能处理-r标记。该标记表明,以逆序(递减)方式排序。要保证-r和-n能够组合在一起使用。
Exercise 5-15. Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal.
练习5-15 增加选项-f,使得排序过程不考虑字母大小写之间的区别。例如,比较a和A时认为它们相等。
Exercise 5-16. Add the -d ("directory order") option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f.
练习5-16 增加选项-d(代表目录顺序)。该选项表明,只对字母、数字和空格进行比较。要保证该选项可以和-f组合在—起使用。
Exercise 5-17. Add a field-searching capability, so sorting may bee done on fields within lines, each field sorted according to an independent set of options. (The index for this book was sorted with -df for the index category and -n for the page numbers.)
练习5-17 增加字段处理功能,以使得排序程序可以根据行内的不同字段进行排序。每个字段按照一个单独的选项集合进行排序:(在对本书索引进行排序时,索引条目使用了-df选项,而对页码排序时使用丁—n选项。)