> 声明比较函数时,可以使用正确的类型,然后
> 将其转换为定义好的类型,例如
> 'typedef int (*fptr)(const void *, const void *);在qsort调用中使用,这样可以吗?
> 请不要争论!
好吧,至少需要纠正一下,因为这个问题在 FAQ 列表中已经回答过了。不,你不能这样做。更确切地说,大多数 C 实现可以这样做,但这并不是一个可移植的构造;使用它的代码不符合 C 标准的“严格规范”。
> 我这是直接从 Borland C++ 3.1 编译器的帮助页面上看到的。
大概那些帮助页面只关注在编译器使用的机器上可以运行的内容,或者它们的作者只是弄错了。
> 它似乎有效,而且我不明白为什么它不能
> 移植,但这就是我问的原因 ;-)
现在,这个问题“为什么我不能?”不在 FAQ 列表中。
首先看这段代码,我希望大多数人会同意它明显是错误的。
它为什么是错的(除了“因为标准是这么说的”)?显然,这是因为实现会将 3.14 放入传递双精度数的位置,然后在被调用的函数中,尝试从传递整数的位置提取整数。现在,这些可能相同也可能不相同,但即使它们在同一个位置且大小相同,在 i 中收到的值显然也不是 3.14。也不是
void fun1(i) int i; { ... } main() { ... fun1 (3.14); ... }
(int)3.14即 3,因为代码中没有任何地方调用了转换。如果你用原型声明函数,规则就会改变
这是可以的,因为 3.14 在传递之前被转换为 int。的初始值
void fun2 (int i) { ... } main() { ... fun2 (3.14); ... }
i将是 3。现在考虑
在调用发生时,实现看到的是一个指向函数的指针,该函数的参数是 double,因此它会愉快地将 3.14 放入传递 double 的位置,而不会对其进行转换。在那一点上,它不知道该函数指针实际上是从一个需要不同类型参数的函数派生出来的。因此,这段代码会像第一个示例一样失败。
void fun3 (int i) { ... } main() { void (*funp)(double); ... funp = (void (*)(double)) fun3; funp (3.14); ... }
而且,当然,如果类型转换和函数调用发生在不同的函数中,这没有任何区别
但现在我们已经到了一个形式上等同于被问到的那个了
void fun4 (int i) { ... } void funq (void (*funp) (double)) { funp (3.14); } main() { ... funq ((void (*)(double)) fun4); ... }
funq()对应于qsort(),因为它期望一个参数是具有特定参数类型的函数指针。这里它被提供了一个派生自具有不同参数类型的函数的指针。同样,代码像第一个示例一样失败。上面例子和被问到的情况之间有一个区别。在例子中,类型是
int和和double;在,因为它期望一个参数是具有特定参数类型的函数指针。这里它被提供了一个派生自具有不同参数类型的函数的指针。同样,代码像第一个示例一样失败。这个例子中,它们是两种不同的指针类型。
现在,在当今最常用的 C 实现中,所有指针类型都具有相同的表示形式,并且以相同的方式传递给函数。这个事实掩盖了错误:如果调用者将类型为struct gnu *的指针放入放置该类型指针的位置,并且被调用的函数查看放置类型为const void *的指针的位置,并且它们是同一个位置,并且这两种指针类型具有相同的表示形式,那么代码就会运行。在不这样工作的机器上,它将无法运行。
一开始就写对的代码很容易——请参阅 FAQ 列表中的示例。所以请照做。而且也许可以把这篇文章转给下一个你看到做错的人。
-- Mark Brader "`char **' parameters are packaged in GREEN msb@sq.com envelopes and placed on the FIFTH shelf." SoftQuad Inc., Toronto -- Chris Torek
本文为公有领域。