让我们在Linux(qsort_r
)中看到函数/usr/include/stdlib.h
:
typedef int (*__compar_d_fn_t)(const void *, const void *, void *);
extern void qsort_r (void *__base, size_t __nmemb, size_t __size,
__compar_d_fn_t __compar, void *__arg)
__nonnull ((1, 4));
让我们在Mac中看到函数qsort_r
(/usr/include/stdlib.h
):
void qsort_r(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *));
正如您所看到的,这些声明彼此不同(参数序列)。这太令人惊讶了!抱怨在某个地方解决这个问题是否有效?
答案 0 :(得分:15)
唉,不,不。这种方式已经存在了太长时间,依赖它的代码太多了。在某个地方抱怨解决这个问题是否有效?
我认为潜在的问题是“为什么会发生这些不兼容性”?我会回答的。它似乎归结为BSD首先实现它,但界面很差。 ISO和后来的GNU修复了界面,并确定兼容性破坏是值得的。微软会做任何他们想做的事情。
正如@Downvoter(伟大的名字)所指出的那样,qsort_r
是一个非标准的功能。如果它是标准的会很好,但你不能依赖它。 qsort_s
在C11附件K中是标准的,但没有人真正实现C11,更不用说它的附件了,whether Annex K is a good idea is in question。
与许多C和Unix问题一样,这归结为BSD与GNU对比微软以及它们无法协调C扩展。 Linux是GNU。 OS X是许多东西的混合物,但对于C,它遵循BSD。
FreeBSD在2002年9月添加了qsort_r
.Visual Studio 2005的特色略有不同qsort_s
。 ISO在2007年再次正式化了qsort_s
。最后,GNU于2008年在glibc 2.8中出现,明显遵循ISO。 Here's an old thread spanning 2004 to 2008 requesting qsort_r
be implemented in glibc有一些理由。
为了提醒大家,这里是qsort
,如C99中所定义。
void qsort(
void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *)
);
FreeBSD是2002年9月的第一个。他们决定qsort_r
应该打破qsort
接口并将“thunk”参数放在比较函数之前。
void qsort_r(
void *base, size_t nmemb, size_t size,
void *thunk,
int (*compar)(void *, const void *, const void *)
);
为什么呢?您必须询问编写补丁的Garrett Wollman。看看the patch你可以从他对CMP
的变化中看到,我们认为首先使用“thunk”是一个很好的模式。也许他们决定“比较功能到底”是人们会记住的。不幸的是,这意味着qsort
和qsort_r
的比较函数的参数相反。非常混乱。
qsort_s
in Visual Studio 2005。
void qsort_s(
void *base, size_t num, size_t width,
int (__cdecl *compare )(void *, const void *, const void *),
void * context
);
“s”表示“安全”,而不是“r”表示“其他人正在使用的可重入”,可能遵循ISO惯例(见下文),反之亦然。他们将“thunk”放在qsort_s
的末尾,保持参数与qsort
相同,但是为了最大程度的混淆,“thunk”在比较函数的开头就像BSD一样。他们选择了最糟糕的选择。
更糟糕的是,2007年ISO发布了TR 24731-1来向C标准库添加边界检查(感谢@JonathanLeffler指出这一点)。是的,他们有自己的qsort_r
,但它被称为qsort_s
!是的,它与其他人不同!
errno_t qsort_s(
void *base, rsize_t nmemb, rsize_t size,
int (*compar)(const void *x, const void *y, void *context),
void *context
);
他们明智地决定将论据保持在qsort_s
,其比较函数是qsort
的超集,可能认为人们更容易记住。他们增加了一个回报值,可能是一个好主意。更令人困惑的是,当时这是一份“技术报告”,而不是C标准的一部分。它现在是C11标准的“附件K”,仍然是可选的,但承载更多。
GNU决定相同,可能遵循ISO的qsort_s
。
void qsort_r(
void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *, void *),
void *arg
);
看the glibc patch adding qsort_r
它可能也更容易实现。要确定你必须问Ulrich Drepper。
BSD决定用qsort
及其比较函数交换参数可能会在过去几年引起很多混乱和错误。 ISO / GNU保持相同的决定可以说是更好的。 ISO决定给它一个不同的名字。 GNU决定破坏与BSD功能的兼容性。微软决定做任何事情。现在我们遇到了四个不兼容的实现。由于比较函数具有不同的签名,因此兼容性宏非常重要。
(这完全是代码的重构。对于他们的实际理由,你将不得不通过邮件列表档案挖掘。)
我不能真的责怪GNU或BSD或ISO或Microsoft ......好吧,我可以责怪微软故意试图杀死C.点是标准化C的过程,并扩展该标准,让编译器跟随该标准非常缓慢,编译器编写者有时必须做的是权宜之计。
答案 1 :(得分:2)
正如here所述,qsort
已标准化(C99),但qsort_r
是GNU扩展(" qsort_r()
已添加到版本2.8的glibc中&#34)。因此,没有要求跨平台保持相同,更不用说便携了。