c - 在Mac和Linux上qsort_r的不同声明

标签 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 *));

如您所见,这些声明彼此不同(参数顺序)。这真是令人惊讶!在某个地方提示解决这个问题是否有效?

最佳答案

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”是个好模式。也许他们认为“比较功能要走到最后”是人们会记住的。不幸的是,这意味着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)的开头。他们选择了最糟糕的选择。

更糟糕的是,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/

相关文章:

Linux Bash Number Range echo 语法错误

linux - 停止执行在 Linux 服务器端运行的程序

macos - 将字形/字符从一个字体文件复制/粘贴到另一个

逗号运算符在 C 中与赋值运算符的用法

c++ - 使用 Visual Studio 2015 Win64 编译 MySQL Connector

c - sscanf 循环段错误

c - x86-64 System V abi - 参数传递的参数分类

ios - 附近使用 Swift 3.0 的蓝牙设备

c++ - GMP 库 6.0.0 无法在 Mac OS Mavericks 上编译

c - 硬件仿真的正确实现是什么?