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

发布于 2024-10-05 02:24:22 字数 215 浏览 0 评论 0原文

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

qsort_r() is the re-entrant version of qsort() which takes an additional 'thunk' argument and passes it into the compare function and I'd like to be able to use it in portable C code. qsort() is POSIX and everywhere but qsort_r() seems to be a BSD extension. As a specific question, does this exist or have an equivalent in the Windows C runtime?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

渡你暖光 2024-10-12 02:24:22

我尝试编写一个可移植版本的 qsort_r / qsort_s (称为 sort_r),并举例说明。我还将此代码放入 git 存储库 (https://github.com/noporpoise/sort_r)

struct sort_r_data
{
  void *arg;
  int (*compar)(const void *a1, const void *a2, void *aarg);
};

int sort_r_arg_swap(void *s, const void *aa, const void *bb)
{
  struct sort_r_data *ss = (struct sort_r_data*)s;
  return (ss->compar)(aa, bb, ss->arg);
}

void sort_r(void *base, size_t nel, size_t width,
            int (*compar)(const void *a1, const void *a2, void *aarg), void *arg)
{
  #if (defined _GNU_SOURCE || defined __GNU__ || defined __linux__)

    qsort_r(base, nel, width, compar, arg);

  #elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
         defined __FREEBSD__ || defined __BSD__ || \
         defined OpenBSD3_1 || defined OpenBSD3_9)

    struct sort_r_data tmp;
    tmp.arg = arg;
    tmp.compar = compar;
    qsort_r(base, nel, width, &tmp, &sort_r_arg_swap);

  #elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)

    struct sort_r_data tmp = {arg, compar};
    qsort_s(*base, nel, width, &sort_r_arg_swap, &tmp);

  #else
    #error Cannot detect operating system
  #endif
}

示例用法:

#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");
}

编译/run/output:

$ 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

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

I've attempted to write a portable version of qsort_r / qsort_s (called sort_r) shown with an example. I've also put this code in a git repo (https://github.com/noporpoise/sort_r)

struct sort_r_data
{
  void *arg;
  int (*compar)(const void *a1, const void *a2, void *aarg);
};

int sort_r_arg_swap(void *s, const void *aa, const void *bb)
{
  struct sort_r_data *ss = (struct sort_r_data*)s;
  return (ss->compar)(aa, bb, ss->arg);
}

void sort_r(void *base, size_t nel, size_t width,
            int (*compar)(const void *a1, const void *a2, void *aarg), void *arg)
{
  #if (defined _GNU_SOURCE || defined __GNU__ || defined __linux__)

    qsort_r(base, nel, width, compar, arg);

  #elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
         defined __FREEBSD__ || defined __BSD__ || \
         defined OpenBSD3_1 || defined OpenBSD3_9)

    struct sort_r_data tmp;
    tmp.arg = arg;
    tmp.compar = compar;
    qsort_r(base, nel, width, &tmp, &sort_r_arg_swap);

  #elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)

    struct sort_r_data tmp = {arg, compar};
    qsort_s(*base, nel, width, &sort_r_arg_swap, &tmp);

  #else
    #error Cannot detect operating system
  #endif
}

Example usage:

#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");
}

Compile/run/output:

$ 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

I've tested on mac & linux. Please update this code if you spot mistakes / improvement. You are free to use this code as you wish.

你与昨日 2024-10-12 02:24:22

对于 Windows,您将使用 qsort_s http://msdn.microsoft.com/en-us/library/4xc60xas(VS.80).aspx

显然,关于 BSD 和 GNU 的 qsort_r 版本不兼容存在一些争议,因此在生产代码中使用它时要小心: http://sourceware.org /ml/libc-alpha/2008-12/msg00003.html

顺便说一句,_s 代表“安全”,_r 代表“可重入” ”,但两者都意味着有一个额外的参数。

For Windows you would use qsort_s: http://msdn.microsoft.com/en-us/library/4xc60xas(VS.80).aspx

Apparently there is some controversy about BSD and GNU having incompatible versions of qsort_r, so be careful about using it in production code: http://sourceware.org/ml/libc-alpha/2008-12/msg00003.html

BTW, the _s stands for "secure" and the _r stands for "re-entrant", but both mean that there's an extra parameter.

情感失落者 2024-10-12 02:24:22

任何可移植性标准都没有指定它。另外,我认为将其称为 qsort 的“线程安全”版本是错误的。标准 qsort 是线程安全的,但 qsort_r 有效地允许您传递闭包作为比较函数。

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

It's not specified in any portability standard. Also I think it's a mistake to call it a "thread-safe" version of qsort. The standard qsort is thread-safe, but qsort_r effectively allows you to pass a closure as your comparison function.

Obviously in a single-threaded environment, you can achieve the same result with a global variable and qsort, but this usage will not be thread-safe. A different approach to thread-safety would be to use thread-specific data and have your comparison function retrieve its parameter from the thread-specific data (pthread_getspecific with POSIX threads, or __thread variables in gcc and the upcoming C1x standard).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文