我正在尝试使用标准库的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);
}
...但是当参数不是wchar_t *
类型时,这个看起来应该有错误处理。
答案 0 :(得分:8)
你已经做了很多正确的方式。 The gcc documentation for strcoll
and wcscoll
给出了一个与此类似的示例,以及使用strcoll
或wcscoll
和{的正确方法{1}}。
qsort
这个例子实际上确实引发了你想要摆脱的警告,但是通过将 /* 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);
}
中的char**
更改为const void*
,可以解决这个问题,并且然后显式地转换为compare_elements
。
你是正确的观察到这是类型不安全的,但类型安全并不是C的优点之一。 C没有像泛型或模板那样的东西,所以qsort可以在任意类型上工作的唯一方法是让它的比较函数接受const char**
s。程序员可以确保比较函数不会在可能传递非预期类型的参数的上下文中使用。
那就是说,您的代码中存在错误。比较函数接收的不是要比较的元素,而是指向要比较的元素的指针。因此,如果元素是字符串,那意味着指向指针。所以当你写
void*
当您希望return wcscoll(arg1, arg2);
时,您实际上正在传递wscoll
wchar_t**
。在抑制警告的同时执行此操作的正确方法是:
wchar_t*
像那样丑陋。
修改强>
再看一下代码的最高位。你的错误在这里真是双重的。您正尝试使用int widecharcomp(const void *arg1, const void *arg2)
{
return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}
对字符进行排序。它是一个用于排序字符串的函数(在C中是指向以空字符结尾的字符序列的指针)。上面的内容是假设您尝试对字符串进行排序而编写的。如果您要对字符进行排序,则wcscoll
不适合使用,但上述关于wcscoll
的所有内容仍适用。
答案 1 :(得分:4)
有两个问题:您混淆了wchar_t
和wchar_t*
,并且您试图将wchar_t*
作为void*
传递。
首先,您已告诉qsort
对wchar_t
数组进行排序。但wcscoll
不比较wchar_t
,它会比较类型为wchar_t*
的宽字符串。您的比较似乎有效的事实是由于您的测试数据恰好在两种解释下都能正常工作。
如果你想对字符进行排序,你需要调用一个合适的函数(我不太了解广泛的字符API,告诉你哪一个)。如果要对字符串进行排序,则需要分配一个字符串数组(类型为wchar_t *
)。
此外,即使您有一个wchar_t*
数组,也无法将wcscoll
作为参数传递给qsort
。问题在于无法保证wchar_t*
和void*
具有相同的表示形式。有些机器的字指针与字节指针的表示方式不同;在这样的机器上,qsort
会将字节指针传递给数组的元素wcscoll
,这是行不通的,因为wcscoll
需要字节指针。解决方案是编写一个简单的包装函数,在必要时执行转换。 qsort
通常需要一个简单的包装器。
答案 2 :(得分: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);
不幸的是,直接C qsort()
不能是类型安全的,因此它不能具有“当参数不是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
的{{1}}未正确终止 - 您需要在初始化程序结束时使用wcslen()
:
0
答案 3 :(得分:0)
您无法将函数指针强制转换为其他类型,您当前的解决方案就是