C 与qsort相比,可重入qsort_r函数的可移植性如何?

C 与qsort相比,可重入qsort_r函数的可移植性如何?,c,portability,C,Portability,qsort_r()是qsort()的可重入版本,它接受一个额外的'thunk'参数并将其传递到compare函数中,我希望能够在可移植的C代码中使用它qsort()是POSIX和everywhere,但qsort\r()似乎是BSD扩展。作为一个具体的问题,这在Windows C运行时中是否存在或具有等效功能?对于Windows,您将使用qsort\s: 显然,对于BSD和GNU具有不兼容版本的qsort\u r,存在一些争议,因此在生产代码中使用它时要小心: 顺便说一句,\u s代表“安全”,

qsort_r()
qsort()
的可重入版本,它接受一个额外的'thunk'参数并将其传递到compare函数中,我希望能够在可移植的C代码中使用它
qsort()
是POSIX和everywhere,但
qsort\r()
似乎是BSD扩展。作为一个具体的问题,这在Windows C运行时中是否存在或具有等效功能?

对于Windows,您将使用
qsort\s

显然,对于BSD和GNU具有不兼容版本的
qsort\u r
,存在一些争议,因此在生产代码中使用它时要小心:


顺便说一句,
\u s
代表“安全”,而
\u r
代表“重新进入”,但两者都意味着有一个额外的参数。

在任何便携性标准中都没有规定。此外,我认为将其称为
qsort
的“线程安全”版本是一个错误。标准的
qsort
是线程安全的,但是
qsort\r
有效地允许您传递闭包作为比较函数


显然,在单线程环境中,使用全局变量和
qsort
可以获得相同的结果,但这种用法不是线程安全的。线程安全的另一种方法是使用特定于线程的数据,并让比较函数从特定于线程的数据中检索其参数(
pthread\u getspecific
使用POSIX线程,或者在gcc和即将推出的C1x标准中使用
\u thread
变量).

我试图编写一个可移植版本的qsort\u r/qsort\u s(称为sort\u r),并给出了一个示例。我还将此代码放在git回购()中

用法示例:

#include <stdio.h>

/* comparison function to sort an array of int, inverting a given region
   `arg` should be of type int[2], with the elements
   representing the start and end of the region to invert (inclusive) */
int sort_r_cmp(const void *aa, const void *bb, void *arg)
{
  const int *a = aa, *b = bb, *p = arg;
  int cmp = *a - *b;
  int inv_start = p[0], inv_end = p[1];
  char norm = (*a < inv_start || *a > inv_end || *b < inv_start || *b > inv_end);
  return norm ? cmp : -cmp;
}

int main()
{
  /* sort 1..19, 30..20, 30..100 */
  int arr[18] = {1, 5, 28, 4, 3, 2, 10, 20, 18, 25, 21, 29, 34, 35, 14, 100, 27, 19};
  /* Region to invert: 20-30 (inclusive) */
  int p[] = {20, 30};
  sort_r(arr, 18, sizeof(int), sort_r_cmp, p);

  int i;
  for(i = 0; i < 18; i++) printf(" %i", arr[i]);
  printf("\n");
}

我已经在mac和linux上进行了测试。如果发现错误/改进,请更新此代码。您可以随意使用此代码

你说得对。它不是线程安全的,它是可重入的。这意味着即使在单线程环境中,您也可能需要它。
qsort
函数本身是可重入的,或者至少它应该在任何正常的实现中。问题在于,使想要调用
qsort
的函数与需要参数的比较函数可重入。是的,
qsort
算法显然不需要全局状态,因此它实际上是可重入的且线程安全的。我的意思是,
qsort\r
用于可能需要重新进入的地方,因此使用线程本地存储并不总是能达到相同的效果。你是对的,它是重新进入的,事实上我不关心线程,我只需要一个带有额外用户数据参数的比较函数,可以用来访问其他状态。对于Windows,您还可以使用线程本地存储API:
TlsGetValue
和friends,或者
\u declspec(thread)
变量说明符。有一个错误:在我的Clang 11+macOS 10.15环境中,
如果(defined _GNU_SOURCE
为true,优先于
#elif(defined(defined(defined)APPLE(defined)
,因此
qsort_r
以错误的顺序与args一起调用。重新排序
#if
分支,以便首先发生APPLE/BSD检测可修复此问题。
#include <stdio.h>

/* comparison function to sort an array of int, inverting a given region
   `arg` should be of type int[2], with the elements
   representing the start and end of the region to invert (inclusive) */
int sort_r_cmp(const void *aa, const void *bb, void *arg)
{
  const int *a = aa, *b = bb, *p = arg;
  int cmp = *a - *b;
  int inv_start = p[0], inv_end = p[1];
  char norm = (*a < inv_start || *a > inv_end || *b < inv_start || *b > inv_end);
  return norm ? cmp : -cmp;
}

int main()
{
  /* sort 1..19, 30..20, 30..100 */
  int arr[18] = {1, 5, 28, 4, 3, 2, 10, 20, 18, 25, 21, 29, 34, 35, 14, 100, 27, 19};
  /* Region to invert: 20-30 (inclusive) */
  int p[] = {20, 30};
  sort_r(arr, 18, sizeof(int), sort_r_cmp, p);

  int i;
  for(i = 0; i < 18; i++) printf(" %i", arr[i]);
  printf("\n");
}
$ gcc -Wall -Wextra -pedantic -o sort_r sort_r.c
$ ./sort_r
 1 2 3 4 5 10 14 18 19 29 28 27 25 21 20 34 35 100