一尘不染

在Mac和Linux上qsort_r的不同声明

linux

让我们看看qsort_rLinux(/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));

让我们看看qsort_rMac(/usr/include/stdlib.h)中的功能:

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

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


阅读 505

收藏
2020-06-07

共1个答案

一尘不染

在某个地方抱怨解决这个问题是否有效?

las,不。这种方式已经存在了很长时间,并且有太多的代码依赖于此。

我认为根本的问题是“ 为什么会发生这些不兼容
”?我会回答。它似乎可以归结为BSD首先实现它,但界面较差。ISO和更高版本的GNU修复了该接口,并认为兼容性破坏是值得的。微软会尽其所能。

正如@Downvoter(大名)所指出的那样,它qsort_r是一个非标准函数。如果它是标准的,那就太好了,但是您不能依靠它。qsort_sC11是C11附件K中的一种标准,但是没有人真正实现C11,更不用说其附件了,并且附件K是否是一个好主意还值得商 .。

像许多C和Unix问题一样,这归结为BSD,GNU和Microsoft以及它们无法协调C扩展。Linux是GNU。OS
X杂乱无章,但对于C而言,它遵循BSD。

FreeBSD qsort_r是2002年9月添加的。VisualStudio 2005的功能略有不同qsort_s。ISO
qsort_s在2007年再次正式化,但又有所不同。最终,GNU在几年后于2008年在glibc
2.8中出现,显然是在遵循ISO。这是一个跨越2004年到2008年的旧线程,要求qsort_r在glibc实现,它具有一些基本原理。

为了提醒大家,这是qsortC99中定义的。

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
。看一下补丁,您可以从他的更改中看到,CMP决定首先拥有“
thunk”是一个很好的模式。也许他们认为“比较功能要走到最后”是人们会记住的。不幸的是,这意味着qsortqsort_r比较函数的参数相反。非常混乱。


同时,曾经是创新者的Microsoft qsort_s在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
);

查看glibc补丁,添加qsort_r它可能也更容易实现。要确定要知道,您必须询问Ulrich
Drepper。


qsort多年来,BSD决定与之交换参数的决定及其比较功能可能已引起许多混乱和错误。ISO /
GNU决定使它们保持相同的决定无疑会更好。ISO决定给它起一个不同的名字。GNU决定破坏与BSD功能的兼容性。微软决定做任何事情。现在,我们陷入了四个不兼容的实现中。由于比较函数具有不同的签名,因此兼容性宏是很重要的。

(这都是对代码的重新构造。出于其实际原理,您必须深入研究邮件列表档案。)

我不能真的怪罪GNU或BSD或ISO或微软…好吧,我可以怪罪微软故意杀死C。Point是标准化C,扩展该标准,并使编译器遵循该标准的过程。痛苦的速度很慢,编译器编写者有时不得不做一些权宜之计。

2020-06-07