当前位置: 首页 > 面试题库 >

在Mac和Linux上qsort_r的不同声明

袁宜民
2023-03-14
问题内容

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

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


问题答案:

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

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,扩展该标准,并使编译器遵循该标准的过程。痛苦的速度很慢,编译器编写者有时不得不做一些权宜之计。



 类似资料:
  • 写这篇文章的原因是再次阅读王珢《谈 Linux,Windows 和 Mac》有感。 Linux 和 Unix 里面包含了一些非常糟糕的设计。 毫无疑问,但是很多 Linux 狂热分子却对此避而不谈,赞美(正面意义)的文章却广为传播,因此初学者容易留下刻板的印象。作为一个(桌面领域)需要推广的操作系统可能并不是错误的决定,但是对于真正想要学习 Linux 和 Unix 哲学的人会造成负面影响。 另外

  • 问题内容: 我有一个从腌制文件中导入数据的应用程序。它在Windows中可以正常工作,但Mac和Linux的行为很奇怪。 在OS X中,除非将文件类型设置为。,否则腌制的文件(文件扩展名“ .char”)不可用。然后,如果我选择一个扩展名为.char的文件,它将无法加载,并显示错误消息 ValueError:无法将字符串转换为浮点型 但是,如果我创建的文件没有.char扩展名,则该文件将正常加载。

  • 我已经从Linux(Ubuntu)迁移到Mac OS。我用Java(swing)编写了一个应用程序。我已经成功地运行了同样的代码在windows和linux但在mac os我有一些问题。 下面是注释中的代码和问题: 另一个: 我当然编不出来。 Java版本:Ubuntu: java版本“1.7.0_07”java(TM)SE运行时环境(build 1.7.0_07-B10)java HotSpot

  • 问题内容: 是否可以在Linux和Mac上调用无需备份的待办事情就地编辑?虽然似乎需要OS X附带的BSD ,但是GNU Linux发行版通常随附将引号解释为空的输入文件名(而不是备份扩展名),而是需要使用引号。 是否有任何两种语法都适用的命令行语法,所以我可以在两个系统上使用相同的脚本? 问题答案: 如果您真的只想使用“简单”的方法,那么以下DOES可以在GNU和BSD / Mac上运行: 注意

  • 问题内容: 据我所知,Java使用的是操作系统线程(与Erlang相反),这意味着在Windows和Linux上用Java创建的线程的行为可能有所不同。 Windows和Linux上的Java线程是否有差异?最大的区别是什么?可能只是性能上的差异? 问题答案: 这是一个非常笼统的问题,所以我将给出一个一般性的答案。 Java 在其开发的早期就从绿色线程转换为本地线程。这并不意味着在Windows和