Mac和Linux上qsort_r的不同声明

时间:2016-09-18 17:44:33

标签: c linux macos

让我们在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 *));

正如您所看到的,这些声明彼此不同(参数序列)。这太令人惊讶了!抱怨在某个地方解决这个问题是否有效?

2 个答案:

答案 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”是一个很好的模式。也许他们决定“比较功能到底”是人们会记住的。不幸的是,这意味着qsortqsort_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)。因此,没有要求跨平台保持相同,更不用说便携了。