C语言技巧—怎样用指向函数的指针作函数的参数?
函数的指针可以作为一个参数传递给另外一个函数,这一点非常有意思。一个函数用函数指针作参数,意味着这个函数的一部分工作需要通过函数指针调用另外的函数来完成,这被称为“回调(callback)”。处理图形用户接口的许多C库函数都用函数指针作参数,因为创建显示风格的工作可以由这些函数本身完成,但确定显示内容的工作需要由应用程序完成。
举一个简单的例子,假设有一个由字符指针组成的数组,你想按这些指针指向的字符串的值对这些指针进行排序,你可以使用qsort()函数,而qsort()函数需要借助函数指针来完成这项任务(关于排序的详细介绍请参见第3章“排序和查找”。qsort()函数有4个参数:
(1) 指向数组开头的指针;
(2) 数组中的元素数目;
(3) 数组中每个元素的大小;
(4) 指向一个比较函数的指针。
qsort()函数返回一个整型值。
比较函数有两个参数,分别为指向要比较的两个元素的指针。当要比较的第一个元素大于、等于或小于第二个元素时,比较函数分别返回一个大于o,等于。或小于。的值。一个比较两个整型值的函数可能如下所示:
int icmp(const int *p1,const int *p2)
{
return *p1-*p2;
}
排序算法和交换算法都是qsort()函数的部分内容。qsort()函数的交换算法代码只负责拷贝指定数目的字节(可能调用memcpy()或memmove()函数),因此qsort()函数不知道要对什么样的数据进行排序,也就不知道如何比较这些数据。比较数据的工作将由函数指针所指向的比较函数来完成。
对本例来说,不能直接用strcmp()函数作比较函数,其原因有两点:第一,strcmp()函数的类型与本例不符(见下文中的介绍);第二,srtcmp()函数不能直接对本例起作用。strcmp()函数的两个参数都是字符指针,它们都被strcmp()函数看作是字符串中的第一个字符;本例要处理的是字符指针(char *s),因此比较函数的两个参数必须都是指向字符指针的指针。本例最好使用下面这样的比较函数;
int strpcmp(const void *p1,const void *p2)
{
char * const *sp1 = (char * const *)p1;
char'const *sp2=(char *const *)p2;
return strcmp(*sp1,*sp2);
}
本例对qsort()函数的调用可以如下所示:
qsort(array,numElements,sizeof(char *),pf2);
这样,每当qsort()函数需要比较两个字符指针时,它就可以调用strpcmp()函数了。
为什么不能直接将strcmp()函数传递给qsort()函数呢?为什么strpcmp()函数中的参数是如此一种形式呢?因为函数指针的类型是由它所指向的函数的返回值类型及其参数的数目和类型共同决定的,而qsort()函数要求比较函数含两个const void *类型的参数:
void qsort(void *base,
size_t numElernents,
size_t sizeOfElement,
int(*compFunct)(const void *,const void *));
qsort()函数不知道要对什么样的数据进行排序,因此,base参数和比较函数中的两个参数都是void指针。这一点很容易理解,因为任何指针都能被转换成void指针,并且不需要强制转换。但是,qsort()函数对函数指针参数的类型要求就苛刻一些了。本例要排序的是一个字符指针数组,尽管strcmp()函数的比较算法与此相符,但其参数的类型与此不符,所以在本例中strcmp()函数不能直接被传给qsort()函数。在这种情况下,最简单和最安全的方法是将一个参数类型符合qsort()函数的要求的比较函数传给qsort()函数,而将比较函数的参数强制转换成strcmp()函数所要求的类型后再传给strcmp()函数;strpcmp()函数的作用正是如此。
不论C程序在什么样的环境中运行,char *类型和void。类型之间都能进行等价的转换,因此,你可以通过强制转换函数指针类型使qsort()函数中的函数指针参数指向strcmp()函数,而不必另外定义一个strpcmp()这样的函数,例如:
char table[NUM_ELEMENTS][LEMENT_SIZE);
/* ... */
/* passing strcmp() to qsort for array Of array Of char */
qsort(table,NUM_ELEMENTS,ELEMENT_SIZE,
(int(*)(const void *,const void *))strcmp);
不管是强制转换strpcmp()函数的参数的类型,还是强制转换指向strcmp()函数的指针的类型,你都必须小心进行,因为稍有疏忽,就会使程序出错。在实际编程中,转换函数指针的类型更容易使程序出错。
举一个简单的例子,假设有一个由字符指针组成的数组,你想按这些指针指向的字符串的值对这些指针进行排序,你可以使用qsort()函数,而qsort()函数需要借助函数指针来完成这项任务(关于排序的详细介绍请参见第3章“排序和查找”。qsort()函数有4个参数:
(1) 指向数组开头的指针;
(2) 数组中的元素数目;
(3) 数组中每个元素的大小;
(4) 指向一个比较函数的指针。
qsort()函数返回一个整型值。
比较函数有两个参数,分别为指向要比较的两个元素的指针。当要比较的第一个元素大于、等于或小于第二个元素时,比较函数分别返回一个大于o,等于。或小于。的值。一个比较两个整型值的函数可能如下所示:
int icmp(const int *p1,const int *p2)
{
return *p1-*p2;
}
排序算法和交换算法都是qsort()函数的部分内容。qsort()函数的交换算法代码只负责拷贝指定数目的字节(可能调用memcpy()或memmove()函数),因此qsort()函数不知道要对什么样的数据进行排序,也就不知道如何比较这些数据。比较数据的工作将由函数指针所指向的比较函数来完成。
对本例来说,不能直接用strcmp()函数作比较函数,其原因有两点:第一,strcmp()函数的类型与本例不符(见下文中的介绍);第二,srtcmp()函数不能直接对本例起作用。strcmp()函数的两个参数都是字符指针,它们都被strcmp()函数看作是字符串中的第一个字符;本例要处理的是字符指针(char *s),因此比较函数的两个参数必须都是指向字符指针的指针。本例最好使用下面这样的比较函数;
int strpcmp(const void *p1,const void *p2)
{
char * const *sp1 = (char * const *)p1;
char'const *sp2=(char *const *)p2;
return strcmp(*sp1,*sp2);
}
本例对qsort()函数的调用可以如下所示:
qsort(array,numElements,sizeof(char *),pf2);
这样,每当qsort()函数需要比较两个字符指针时,它就可以调用strpcmp()函数了。
为什么不能直接将strcmp()函数传递给qsort()函数呢?为什么strpcmp()函数中的参数是如此一种形式呢?因为函数指针的类型是由它所指向的函数的返回值类型及其参数的数目和类型共同决定的,而qsort()函数要求比较函数含两个const void *类型的参数:
void qsort(void *base,
size_t numElernents,
size_t sizeOfElement,
int(*compFunct)(const void *,const void *));
qsort()函数不知道要对什么样的数据进行排序,因此,base参数和比较函数中的两个参数都是void指针。这一点很容易理解,因为任何指针都能被转换成void指针,并且不需要强制转换。但是,qsort()函数对函数指针参数的类型要求就苛刻一些了。本例要排序的是一个字符指针数组,尽管strcmp()函数的比较算法与此相符,但其参数的类型与此不符,所以在本例中strcmp()函数不能直接被传给qsort()函数。在这种情况下,最简单和最安全的方法是将一个参数类型符合qsort()函数的要求的比较函数传给qsort()函数,而将比较函数的参数强制转换成strcmp()函数所要求的类型后再传给strcmp()函数;strpcmp()函数的作用正是如此。
不论C程序在什么样的环境中运行,char *类型和void。类型之间都能进行等价的转换,因此,你可以通过强制转换函数指针类型使qsort()函数中的函数指针参数指向strcmp()函数,而不必另外定义一个strpcmp()这样的函数,例如:
char table[NUM_ELEMENTS][LEMENT_SIZE);
/* ... */
/* passing strcmp() to qsort for array Of array Of char */
qsort(table,NUM_ELEMENTS,ELEMENT_SIZE,
(int(*)(const void *,const void *))strcmp);
不管是强制转换strpcmp()函数的参数的类型,还是强制转换指向strcmp()函数的指针的类型,你都必须小心进行,因为稍有疏忽,就会使程序出错。在实际编程中,转换函数指针的类型更容易使程序出错。