投诉某个地方解决这个问题会有效吗?
唉,不会。这种情况已经存在太久了,有太多的代码依赖它。
我认为根本问题是“为什么会出现这些不兼容性”?我来回答一下。似乎归结于BSD最先实现了它,但界面较差。ISO和后来的GNU修复了界面并决定破坏兼容性是值得的。而微软则做他们想做的。
正如@Downvoter(很好的名字)所指出的,qsort_r
是一个非标准函数。如果它成为标准就好了,但你不能依赖它。在C11附录K中,qsort_s
算是标准,但没有人真正实现过C11,更别说它的附录了,而 Annex K 是否是一个好主意还有待商榷。
像许多C和Unix问题一样,这归结于BSD vs GNU vs Microsoft以及它们协调C扩展的无能力。Linux是GNU。OS X是许多东西的混合体,但对于C,它遵循BSD。
自2002年9月起,FreeBSD增加了qsort_r
。Visual Studio 2005则提供了略有不同的qsort_s
。ISO在2007年规范化了另一种略有不同的qsort_s
。最后,GNU在2008年的glibc 2.8中推出了自己的版本,显然是遵循了ISO的标准。这里有一个跨越2004年至2008年的旧帖,要求在glibc中实现qsort_r
,其中包含一些理由说明。
为了提醒大家,这里是C99中定义的qsort
。
void qsort(
void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *)
);
FreeBSD在2002年9月率先采用了新的qsort_r
函数。他们决定在比较函数之前添加“thunk”参数,从而打破了qsort
接口。
void qsort_r(
void *base, size_t nmemb, size_t size,
void *thunk,
int (*compar)(void *, const void *, const void *)
);
为什么?你需要问编写补丁的Garrett Wollman。从该补丁中可以看出,他对CMP
进行的更改决定了将“thunk”放在前面是一个好的模式。也许他们认为“比较函数放在最后”是人们会记住的。不幸的是,这意味着qsort
和qsort_r
的比较函数参数被反转了。非常令人困惑。
与此同时,始终保持创新的微软公司在Visual Studio 2005中推出了qsort_s
。
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
);
看着添加了 qsort_r
的 glibc 补丁,可能也更容易实现。要确定,你需要问 Ulrich Drepper。
BSD决定与qsort交换参数和比较函数可能在多年内引起了许多混乱和错误。 ISO / GNU决定保持它们相同,这可能更好。 ISO决定给它一个不同的名称。 GNU决定破坏与BSD函数的兼容性。 Microsoft决定做任何事情。现在我们面临着四个不兼容的实现。由于比较函数具有不同的签名,兼容性宏是非平凡的。
(这都是从代码重建出来的。要了解他们的实际原理,您需要查阅邮件列表档案。)
我真的不能责怪GNU或BSD或ISO或Microsoft……好吧,我可以责怪Microsoft故意试图杀死C。问题是标准化C的过程,扩展该标准以及使编译器遵循该标准的过程非常缓慢,编译器编写者有时必须做 expedient 的事情。