Q现在我正尝试对一个结构体数组进行排序,使用qsort.我的比较函数接收结构指针,但编译器抱怨该函数对于qsort来说类型不正确。如何转换函数指针以关闭警告?
A转换必须在比较函数中进行,该函数必须声明为接受“通用指针”(const void *),如上面第 13.8 题所述。对于一个假设的简单日期结构体
struct mystruct { int year, month, day; };比较函数可能看起来像这样 [脚注]
int mystructcmp(const void *p1, const void *p2) { const struct mystruct *sp1 = p1; const struct mystruct *sp2 = p2; if(sp1->year < sp2->year) return -1; else if(sp1->year > sp2->year) return 1; else if(sp1->month < sp2->month) return -1; else if(sp1->month > sp2->month) return 1; else if(sp1->day < sp2->day) return -1; else if(sp1->day > sp2->day) return 1; else return 0; }(从通用指针到struct mystruct指针的转换发生在初始化时sp1 = p1和sp2 = p2;编译器会隐式执行这些转换,因为p1和p2是void指针。)
对于此版本的mystructcmp,调用qsort可能看起来像这样
#include <stdlib.h> struct mystruct dates[NDATES]; int ndates; /* ndates cells of dates[] are to be sorted */ qsort(dates, ndates, sizeof(struct mystruct), mystructcmp);
另一方面,如果您正在排序结构体指针,则需要间接引用,如第 13.8 题所述;比较函数的头部应该看起来像这样
int myptrstructcmp(const void *p1, const void *p2) { struct mystruct *sp1 = *(struct mystruct * const *)p1; struct mystruct *sp2 = *(struct mystruct * const *)p2;调用应该看起来像这样
struct mystruct *dateptrs[NDATES]; qsort(dateptrs, ndates, sizeof(struct mystruct *), myptrstructcmp);
要理解在qsort比较函数中进行这些奇怪的指针转换的必要性(以及为什么在调用时转换函数指针qsort无济于事),了解qsort如何工作很有帮助。qsort不知道它正在排序的数据的类型或表示:它只是在移动内存的小块。(它唯一知道的关于这些块的信息是它们的大小,您在qsort的第三个参数中指定。)为了确定两个块是否需要交换,qsort会调用您的比较函数。(为了交换它们,它使用等同于memcpy.)
由于qsort以通用方式处理未知类型的内存块,因此它使用通用指针(void *)来引用它们。当qsort调用您的比较函数时,它会将要比较的块的两个通用指针作为参数传递。由于它传递的是通用指针,因此您的比较函数必须*接受*通用指针,并在操作它们(即执行比较)之前将指针转换回其适当的类型。一个void指针与结构体指针不是同一类型,并且在某些机器上可能具有不同的尺寸或表示(这就是为什么这些强制类型转换对于正确性是必需的)。
如果您要对结构体数组进行排序,并且有一个接受结构体指针的比较函数
int mywrongstructcmp(struct mystruct *, struct mystruct *);并且您调用了qsort为
qsort(dates, ndates, sizeof(struct mystruct), (int (*)(const void *, const void *))mywrongstructcmp); /* WRONG */转换(int (*)(const void *, const void *))除了可能抑制编译器关于此比较函数可能*不*与qsort一起使用的消息之外,别无他用。您在调用 qsort 时使用的任何转换的影响,将在qsort调用您的比较函数时被遗忘:它将用const void *参数调用它们,因此您的函数必须接受这些参数。不存在任何原型机制可以在qsort内部操作,以便在调用voidstruct mystruct指针之前将其转换为指针。mywrongstructcmp.
一般来说,为了“让编译器闭嘴”而插入强制类型转换是一个坏主意。编译器警告通常在告诉你一些事情,而且除非你真的知道你在做什么,否则忽视或压制它们会让你承担风险。另请参阅问题 4.9。
参考:ISO Sec. 7.10.5.2
H&S Sec. 20.5 p. 419