简体   繁体   English

Mac和Linux上qsort_r的不同声明

[英]Different declarations of qsort_r on Mac and Linux

Let's see function qsort_r in Linux ( /usr/include/stdlib.h ): 让我们看qsort_r 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));

Let's see function qsort_r in Mac ( /usr/include/stdlib.h ): 让我们看qsort_r Mac中的函数qsort_r/usr/include/stdlib.h ):

void qsort_r(void *, size_t, size_t, void *, int (*)(void *, const void *, const void *));

As you can see these declarations are differ from each other (sequence of arguments). 正如您所看到的,这些声明彼此不同(参数序列)。 This is surprising! 这太令人惊讶了! Will it be effective to complain somewhere to solve this problem? 抱怨在某个地方解决这个问题会有效吗?

Will it be effective to complain somewhere to solve this problem? 抱怨在某个地方解决这个问题会有效吗?

Alas, no. 唉,没有。 It's been this way for too long and there's too much code relying on it. 这种方式已经存在了太长时间,依赖它的代码太多了。

I think the underlying question is " why do these incompatibilities happen "? 我认为根本问题是“ 为什么会发生这些不相容 ”? I'll answer that. 我会回答的。 It appears to boil down to BSD implementing it first but with a poor interface. 它似乎归结为BSD首先实现它,但界面很差。 ISO and later GNU fixed the interface and decided the compatibility breakage was worth it. ISO和后来的GNU修复了界面,并确定兼容性破坏是值得的。 And Microsoft does whatever they feel like. 微软会做任何他们想做的事情。

As pointed out by @Downvoter (great name), qsort_r is a non-standard function. 正如@Downvoter(伟大的名字)所指出的那样, qsort_r是一个非标准函数。 It would be nice if it were standard, but you can't rely on that. 如果它是标准的会很好,但你不能依赖它。 qsort_s is sort of standard in C11 Annex K, but nobody really implements C11, let alone its annexes, and whether Annex K is a good idea is in question . qsort_s在C11附件K中是一种标准,但是没有人真正实现C11,更不用说它的附件了, 附件K是否是一个好主意是有问题的

Like a lot of C and Unix issues, this comes down to BSD vs GNU vs Microsoft and their inability to coordinate C extensions. 像许多C和Unix问题一样,这归结为BSD与GNU对比微软以及它们无法协调C扩展。 Linux is GNU. Linux是GNU。 OS X is a mish-mash of many things, but for C it follows BSD. OS X是许多东西的混合物,但对于C,它遵循BSD。

FreeBSD added qsort_r in Sept 2002. Visual Studio 2005 featured a slightly different qsort_s . FreeBSD在2002年9月添加了qsort_r Studio 2005的qsort_s略有不同。 ISO formalized a yet different again qsort_s in 2007. Finally GNU's came years later in glibc 2.8 in 2008 apparently following ISO. 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 which has some rationales. 这是一个跨越2004年到2008年的旧线程,要求在glibc实现qsort_r这有一些基本原理。

For remind everyone, here is qsort as defined in C99. 为了提醒大家,这里是C99中定义的qsort

void qsort(
    void *base, size_t nmemb, size_t size,
    int (*compar)(const void *, const void *)
);

FreeBSD was the first in Sept 2002. They decided that qsort_r should break the qsort interface and put the "thunk" argument before the comparison function. 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 *)
);

Why? 为什么? You'll have to ask Garrett Wollman who wrote the patch. 你必须要问编写补丁的Garrett Wollman Looking at the patch you can see from his changes to CMP it was decided that having the "thunk" first was a good pattern. 看看你可以从他对CMP变化中看到的补丁,我们认为首先使用“thunk”是一个很好的模式。 Maybe they decided "the comparison function goes at the end" was what people would remember. 也许他们决定“比较功能到底”是人们会记住的。 Unfortunately this means qsort and qsort_r 's comparison functions have their arguments reversed. 不幸的是,这意味着qsortqsort_r的比较函数的参数相反。 Very confusing. 很混乱。


Meanwhile Microsoft, ever the innovator, has qsort_s in Visual Studio 2005 . 与此同时,作为创新者的qsort_s在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" for "secure" rather than "r" for "reentrant" that everyone else was using possibly following an ISO convention (see below) or vice-versa. “s”代表“安全”而不是“r”代表其他人正在使用的“可重入”,可能遵循ISO惯例(见下文),反之亦然。 They put the "thunk" at the end of qsort_s , keeping the arguments the same as qsort , but for maximum confusion the "thunk" goes at the start of the comparison function like BSD. 他们将“thunk”放在qsort_s的末尾,保持参数与qsort相同,但是为了最大程度的混淆,“thunk”在比较函数的开头就像BSD一样。 They chose the worst possible option. 他们选择了最糟糕的选择。


To make matters worse, in 2007 ISO published TR 24731-1 to add bounds checking to the C standard library (thanks @JonathanLeffler for pointing that out). 更糟糕的是,2007年ISO发布了TR 24731-1 ,将边界检查添加到C标准库中(感谢@JonathanLeffler指出这一点)。 And yes, they have their own qsort_r , but it's called qsort_s ! 是的,他们有自己的qsort_r ,但它被称为qsort_s And yes, it's different from everyone else'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
);

They wisely decided to keep the arguments to qsort_s and its comparison function a superset of qsort probably arguing it would be easier for people to remember. 他们明智地决定保留qsort_s的参数,并且它的比较函数是qsort的超集,可能认为人们更容易记住。 And they added a return value, probably a good idea. 他们增加了一个回报值,可能是一个好主意。 To add to the confusion, at the time this was a "Technical Report" and not part of the C standard. 更令人困惑的是,当时这是一份“技术报告”,而不是C标准的一部分。 It's now "Annex K" of the C11 standard, still optional but carries more weight. 它现在是C11标准的“附件K”,仍然是可选的但承载更多的重量。


GNU decided the same, possibly following ISO's qsort_s . GNU决定相同,可能遵循ISO的qsort_s

void qsort_r(
    void *base, size_t nmemb, size_t size,
    int (*compar)(const void *, const void *, void *),
    void *arg
);

Looking at the glibc patch adding qsort_r it was probably also easier to implement. 看看添加qsort_r的glibc补丁,它可能也更容易实现。 To know for sure you'll have to ask Ulrich Drepper. 要确定你必须问Ulrich Drepper。


BSD's decision to swap arguments with qsort and its comparison function has probably caused a lot of confusion and bugs over the years. BSD决定用qsort及其比较函数交换参数可能会在过去几年引起很多混乱和错误。 The ISO / GNU decision to keep them the same is arguably better. ISO / GNU保持相同的决定可以说是更好的。 ISO decided to give it a different name. ISO决定给它一个不同的名字。 GNU decided to break compatibility with the BSD function. GNU决定破坏与BSD功能的兼容性。 Microsoft decided to do whatever. 微软决定做任何事情。 Now we're stuck with four incompatible implementations. 现在我们遇到了四个不兼容的实现。 Because the comparison functions have different signatures a compatibility macro is non-trivial. 因为比较函数具有不同的签名,所以兼容性宏是非平凡的。

(This is all a reconstruction from the code. For their actual rationales you'll have to dig through mailing list archives.) (这完全是代码的重构。对于他们的实际理由,你将不得不通过邮件列表存档。)

I can't really blame GNU or BSD or ISO or Microsoft... ok, I can blame Microsoft for deliberately trying to kill C. Point is the process of standardizing C, and extending that standard, and getting compilers to follow that standard is painfully slow and the compiler writers sometimes have to do what's expedient. 我不能真的责怪GNU或BSD或ISO或Microsoft ......好吧,我可以责怪微软故意试图杀死C.点是标准化C的过程,并扩展该标准,让编译器遵循该标准是痛苦地慢,编译器编写者有时必须做的是权宜之计。

As written here , qsort is standardized (C99), but qsort_r is a GNU-extension (" qsort_r() was added to glibc in version 2.8"). 正如这里所写, qsort是标准化的(C99),但qsort_r是GNU扩展(“ qsort_r()在版本2.8中添加到glibc”)。 So, there are no requirements for it to be the same across platforms, let alone portable. 因此,没有要求它跨平台相同,更不用说便携了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM