将额外参数传递给qsort的比较器

Jug*_*nte 14 c

我只是想知道是否有办法让我将一个额外的参数传递给我的比较器,然后在我的qsort函数中使用它?

例如,我有这两个比较器(一个按升序排列,另一个按降序排列)

qsort(entries, 3, sizeof(struct entry), compare_desc);

int compare_asc(const void *elem1, const void *elem2)
{
     return strcmp(elem1.name.last, elem2.name.last);
}


int compare_desc(const void *elem1, const void *elem2)
{
     return strcmp(elem2.name.last, elem1.name.last);
}
Run Code Online (Sandbox Code Playgroud)

有没有办法,所以我可以做这样的事情:

int compare(const void *elem1, const void *elem2, const char *order)
{
     if (strcmp(order, "asc") == 0)
         return strcmp(elem1.name.last, elem2.name.last);
     else if (strcmp(order, "desc") == 0)
         return strcmp(elem2.name.last, elem1.name.last);
}
Run Code Online (Sandbox Code Playgroud)

我问的原因是我的排序程序必须接受开关,如果我有2个不同的开关(+ a,-a)分别用于升序和降序,那么我必须制作2个不同的比较器功能.如果我添加更多,它会变得更复杂.有没有办法改进这个程序的设计?

编辑:不允许全局和外部变量.

cle*_*ong 10

老问题,但万一有人偶然发现它......

有一些非标准版本的qsort()可以让你将额外的参数传递给回调函数.glib提供qsort_r(),而VC提供qsort_s().


Ark*_*kku 5

在您的示例中,最好有两个不同的比较器.如果您只有一个,那么每次比较都会不必要地确定排序顺序,无论如何对于任何有意义的结果都无法进行中间排序.所以不要把if (ascending_sort) { } else { }比较器放在里面,而是把它放在你的qsort电话里:

qsort(e, n, sizeof(*e), (strcmp(order, "asc") ? compare_desc : compare_asc));
Run Code Online (Sandbox Code Playgroud)

编辑:如果您添加更多比较器的一些提示:

- 记住你不需要重写每个比较器; 如果你在多个字段上排序,你可以让它们互相调用(你总是可以反转比较器的结果-,例如,compare_asc(a, b)可以返回-compare_desc(a, b)).

- 最终可以很容易地颠倒整个阵列的顺序,因此您不需要将比较器的数量加倍,以支持反转整个排序顺序的选项

- 您可以? :使用函数返回我示例中的三元运算符(),该函数返回下面注释中建议的相应比较器


Jon*_*ler 5

qsort_r()qsort_s()

有函数调用qsort_r()qsort_s()在某些实现中可用的可以执行您想要的操作 - 获取指向传递给比较器函数的额外数据的指针。

BSD 变体实现(包括 macOS 或 Mac OS X)提供了 的一个版本qsort_r(),GNU C 库也是如此。不幸的是,这两种变体具有不同的特征。这并不妨碍它们有用,但这确实意味着不能在两个平台上使用相同的源代码,而且您必须确保了解哪个变体qsort_r()在尝试使用它的任何机器上可用的.

同样,微软提供了一个版本,qsort_s()C11 标准定义了一个版本qsort_s()(作为附件K中的一个可选功能,基于TR-24731),但两者在签名上又有所不同。也许是幸运的是,附件 K 的功能没有被广泛实现。

BSD qsort_r()

void qsort_r(void *base, size_t nel, size_t width, void *thunk,
             int (*compar)(void *, const void *, const void *));
Run Code Online (Sandbox Code Playgroud)

GNU C 库 qsort_r()

void qsort_r(void *base, size_t nmemb, size_t size,
             int (*compar)(const void *, const void *, void *),
             void *arg);
Run Code Online (Sandbox Code Playgroud)

请注意,在 BSD 中,“thunk”相当于 GNU 中的“arg”,但这些参数出现在调用序列中的不同位置 qsort_r()函数(在比较器函数指针之前和之后)。此外,请注意“thunk”作为参数 1 传递给 BSD 比较器函数,但“arg”作为参数 3 传递给 GNU 比较器函数。

助记符qsort_r:上下文数据是相对于调用序列中的比较器指定的,其关系与上下文传递给与被比较的两个值相关的比较器的关系相同。指向比较器的指针之前的上下文意味着调用比较器的值之前的上下文;指向比较器的指针之后的上下文意味着调用比较器的值之后的上下文。

附件K 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);
Run Code Online (Sandbox Code Playgroud)

附件 Kqsort_s()在返回值方面是唯一的;所有其他变体都不返回任何值。否则,出于大多数实际目的,它与 GNUqsort_r()函数。

微软 qsort_s()

void qsort_s(void *base, size_t num, size_t width,
             int (__cdecl *compare )(void *, const void *, const void *),
             void * context);
Run Code Online (Sandbox Code Playgroud)

rsize_tsize_t比较附件K时的区别不是很重要,微软的变种qsort_s(),但在附录K qsort_s(),上下文作为参数3到比较过去了,但在微软qsort_s(),上下文作为参数1到比较过去了。

概括

调用qsort_r()qsort_s()提供所需功能的函数。但是,您必须检查存在哪个函数的平台规范,以及排序函数参数的正确调用序列,以及为参数比较正确的调用序列。

名义上,您也应该检查函数的返回类型,但很少有程序会考虑检查它,主要是因为大多数变体都qsort()没有返回值。


SLa*_*aks 1

在简单的情况下,您可以使用全局变量。

  • 抱歉,我忘了提及,但要求之一是不允许使用全局变量。 (2认同)