4

我正在尝试使用标准库qsort对宽字符数组进行排序:

wchar_t a = L'a';
wchar_t a1 = L'ä';
wchar_t b = L'z';
wchar_t chararray[] = {b, a, a1};  
length = wcslen(chararray);

qsort(chararray, length, sizeof(wchar_t), wcscoll);

现在我认为所涉及的函数有这些原型:

int wcscoll(const wchar_t *ws1, const wchar_t *ws2);
void qsort(void *base, size_t num, size_t size, int (*comp_func)(const void *, const void *))

结果完全符合预期,但为什么我会收到编译器警告passing argument 4 of ‘qsort’ from incompatible pointer type?我怎样才能铸造wcscoll以适应原型?

如果我定义并传入一个单独的比较函数,警告就会消失:

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(arg1, arg2);
}

...但是当参数不是 type 时,这个看起来应该有错误处理wchar_t *

4

4 回答 4

8

你已经做了很多正确的方法。 的 gcc 文档strcollwcscoll给出了一个与此类似的示例,作为正确使用strcollwcscollqsort.

 /* This is the comparison function used with qsort. */

 int
 compare_elements (char **p1, char **p2)
 {
   return strcoll (*p1, *p2);
 }

 /* This is the entry point---the function to sort
    strings using the locale's collating sequence. */

 void
 sort_strings (char **array, int nstrings)
 {
   /* Sort temp_array by comparing the strings. */
   qsort (array, nstrings,
          sizeof (char *), compare_elements);
 }

这个例子实际上确实引发了你想要摆脱的警告,但同样可以通过将参数中的 tochar**更改为,然后显式转换为 来解决它。const void*compare_elementsconst char**

你说这是类型不安全的,但类型安全并不是 C 语言的强项之一。C 没有泛型或模板之类的东西,所以 qsort 可以在任意类型上工作的唯一方法是让它的比较函数接受void*s。程序员有责任确保比较函数不用于可能传递非预期类型的​​参数的上下文中。

也就是说,您的代码中有错误。比较函数接收的不是要比较的元素,而是指向要比较的元素的指针。因此,如果元素是字符串,则意味着指针指向。所以当你写

return wcscoll(arg1, arg2);

当它期望 a 时,您实际上是在传递wscolla 。在抑制警告的同时,执行此操作的正确方法是:wchar_t**wchar_t*

int widecharcomp(const void *arg1, const void *arg2)
{
    return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}

就这么丑。

编辑:

刚刚又看了一下你的代码的顶部。你的错误在这里真的是双重的。您正在尝试使用wcscoll对字符进行排序。这是一个用于对字符串进行排序的函数(在 C 中是指向以 nul 结尾的字符序列的指针)。以上是假设您尝试对字符串进行排序的情况下编写的。如果要对字符进行排序,wcscoll则不适合使用该功能,但上述所有内容qsort仍然适用。

于 2010-08-16T17:28:00.087 回答
4

有两个问题:您混淆了wchar_tand wchar_t*,并且您试图将 a 假冒wchar_t*void*.

首先,您已经告诉qsortwchar_t. 但wcscoll不比较wchar_t,它比较具有 type 的宽字符串wchar_t*。您的比较似乎有效的事实是由于您的测试数据恰好在两种解释下都运行良好。

如果要对字符进行排序,则需要调用适当的函数(我不太了解宽字符 API,无法告诉您是哪一个)。如果要对字符串进行排序,则需要分配一个字符串数组(类型为wchar_t *)。

此外,即使您有一个 数组,也不能将其作为参数wchar_t*可移植地传递给. 问题是不能保证并且具有相同的表示。有些机器的字指针与字节指针有不同的表示;在这样的机器上,会将指向数组元素的字节指针传递给,这不起作用,因为需要字节指针。解决方案是编写一个简单的包装函数,在必要时执行转换。一个简单的包装器通常是必需的。wcscollqsortwchar_t*void*qsortwcscollwcscollqsort

于 2010-08-16T17:36:29.550 回答
2

您已经编写了解决方案(但是,请参阅本文末尾的其他答案和编辑,以选择您正在使用的比较函数和传递给的数据qsort())。

您可以通过将传递给的函数指针转换qsort()为适当的类型来删除包装器函数,但我认为从可维护性的角度来看,使用包装器是一个更好的解决方案。如果您真的想避免包装函数(也许您遇到了可衡量的性能问题),您可以像这样进行转换:

qsort(chararray, length, sizeof(wchar_t), (int(*)(const void*,const void*))wcscoll);

或者使用比较函数类型的 typedef 使其更具可读性:

typedef
int (*comp_func_t)(const void *, const void *);

/* ... */
qsort(chararray, length, sizeof(wchar_t), (comp_func_t) wcscoll);

不幸的是,直接的 Cqsort()不能是类型安全的,所以它不能有“当参数不是 wchar_t 类型时的错误处理”。作为程序员,您有责任确保将正确的数据、大小和比较函数传递给qsort().


编辑:

为了解决其他答案中提到的关于传递给比较函数的类型的一些问题,这里有一个例程,可用于使用当前语言环境的整理序列对 wchar_t 进行排序。图书馆可能有更好的东西,但我现在不知道:

int wchar_t_coll( const void* p1, const void* p2)
{
    wchar_t s1[2] = {0};
    wchar_t s2[2] = {0};

    s1[0] = * (wchar_t*)p1;
    s2[0] = * (wchar_t*)p2;

    return wcscoll( s1, s2);
}

另请注意,chararray您传递给wcslen()的 没有正确终止 - 您需要0在初始化程序的末尾 a :

wchar_t chararray[] = {b, a, a1, 0};  
于 2010-08-16T17:30:10.080 回答
0

您不能将函数指针强制转换为不同的类型,您当前的解决方案与它一样好

于 2010-08-16T17:29:30.470 回答