让我们看一下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 *));
如您所见,这些声明彼此不同(参数顺序)。这真是令人惊讶!在某个地方提示解决这个问题是否有效?
最佳答案
Will it be effective to complain somewhere to solve this problem?
las,不。这种方式已经存在了很长时间,并且有太多的代码依赖于此。
我认为根本的问题是“为什么会发生这些不兼容”?我会回答。它似乎可以归结为BSD首先实现它,但界面较差。 ISO和更高版本的GNU修复了该接口(interface),并认为兼容性破坏是值得的。微软会尽其所能。
正如@Downvoter(大名)所指出的那样,
qsort_r
是非标准函数。如果它是标准的,那就太好了,但是您不能依靠它。 qsort_s
是C11附件K中的一种标准,但是没有人真正实现C11,更不用说其附件和whether Annex K is a good idea is in question了。像许多C和Unix问题一样,这归结为BSD,GNU和Microsoft以及它们无法协调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具有一些基本原理。为了提醒大家,这是C99中定义的
qsort
。void qsort(
void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *)
);
FreeBSD是2002年9月的第一个。他们决定
qsort_r
应该打破qsort
接口(interface),并将“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)的开头。他们选择了最糟糕的选择。更糟糕的是,ISO在2007年发布了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 ...好的,我可以责怪Microsoft故意杀死C。Point是标准化C,扩展该标准并让编译器遵循该标准的过程。缓慢而痛苦的是,编译器编写者有时不得不做些权宜之计。
关于c - 在Mac和Linux上qsort_r的不同声明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39560773/