“不兼容的指针类型” qsort 的第四个参数的编译器警告

发布于 2024-09-14 09:37:23 字数 776 浏览 2 评论 0原文

我正在尝试使用标准库的 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 *))

结果完全符合预期,但为什么我收到编译器警告从不兼容的指针类型传递'qsort'的参数4?如何投射 wcscoll 来适应原型?

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

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

...但是这个函数看起来应该对参数不是 wchar_t * 类型时进行错误处理。

I'm trying to use the standard library's qsort to sort an array of wide characters:

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);

Now I think the functions involved have these prototypes:

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 *))

The results are completely as expected, but why am I getting the compiler warning "passing argument 4 of ‘qsort’ from incompatible pointer type"? And how can I cast wcscoll to fit the prototype?

The warning goes away if I define and pass in a separate comparison function:

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

... but this one looks like it should have error handling for when the arguments are not of type wchar_t *.

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

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

发布评论

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

评论(4

始终不够 2024-09-21 09:37:23

您的做法基本正确。 strcollwcscoll 的 gcc 文档给出了一个与此类似的示例,作为使用 strcoll 或 <代码>wcscoll 和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,然后显式转换为 const char**

您的观察是正确的,这是类型不安全的,但类型安全并不完全是 C 的强项之一。 C 没有泛型或模板之类的东西,因此 qsort 可以处理任意类型的唯一方法是其比较函数接受 void*。程序员有责任确保比较函数不会在可能传递非预期类型的​​参数的上下文中使用。

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

return wcscoll(arg1, arg2);

当需要 wchar_t* 时,您实际上是在传递 wscoll 一个 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 的所有内容仍然适用。

You've done pretty much the right way. The gcc documentation for strcoll and wcscoll gives an example similar to this as the correct way to use strcoll or wcscoll with 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);
 }

This example actually does raise the warning that you want to get rid of, but again it can be gotten around by changing the char** to const void* in the arguments to compare_elements, and then explicitly casting to const char**.

You're right in observing that this is type-unsafe, but type safety is not exactly one of C's strong points. C doesn't have anything like generics or templates, so the only way that qsort can work on an arbitrary type is for its comparison function to accept void*s. It's up to the programmer to make sure that the comparison function is not used in a context where it may be passed arguments that are not the expected type.

That said, there is an error in your code. What the comparison function receives is not the elements to be compared, but rather pointers to the elements to be compared. So if the elements are strings, that means pointer-to-pointer. So when you write

return wcscoll(arg1, arg2);

You are actually passing wscoll a wchar_t** when it expects a wchar_t*. The correct way to do this, while suppressing the warning, would be:

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

as ugly as that is.

Edit:

Just took another look at the top bit of your code. Your error is really twofold here. You're trying to use wcscoll to sort characters. It's a function meant to sort strings (which in C are pointers to nul-terminated sequences of characters). The above was written assuming you were trying to sort strings. If you want to sort characters, then wcscoll is not the appropriate function to use, but everything above regarding qsort still applies.

三生池水覆流年 2024-09-21 09:37:23

有两个问题:您混淆了 wchar_twchar_t*,并且您尝试将 wchar_t* 冒充为 <代码>无效*。

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

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

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

There are two problems: you've mixed up wchar_t and wchar_t*, and you've tried to pass off a wchar_t* as a void*.

First, you've told qsort to sort an array of wchar_t. But wcscoll doesn't compare wchar_t, it compares wide character strings which have the type wchar_t*. The fact that your comparison appears to have worked is due to your test data which just happens to work well under both interpretations.

If you wanted to sort characters, you need to call an appropriate function (I don't know the wide character API well enough to tell you which one). If you wanted to sort strings, you need to allocate an array of strings (of type wchar_t *).

Furthermore, even if you had an array of wchar_t*, you could not portably pass wcscoll as an argument to qsort. The issue is that there is no guarantee that wchar_t* and void* have the same representation. Some machines have word pointers that have a different representation from byte pointers; on such a machine, qsort would pass byte pointers to elements of the array to wcscoll, and this wouldn't work because wcscoll expects byte pointers. The solution is to write a trivial wrapper function that performs the conversion if necessary. A trivial wrapper is often necessary with qsort.

鸠魁 2024-09-21 09:37:23

您已经编写了解决方案(但是,请参阅本文末尾的其他答案和编辑,了解您正在使用的比较函数的选择以及传递给 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);
}

另请注意,您传递给 wcslen()chararray 不是正确终止 - 在初始化程序的末尾需要一个 0

wchar_t chararray[] = {b, a, a1, 0};  

You've coded up your solution already (however, see other answers and edits at the end of this one about with the choice of the comparison function you're using and the data being passed to qsort()).

You could drop the wrapper function by casting the function pointer you pass to qsort() to the appropriate type, but I think using a wrapper is a better solution from a maintainability perspective. If you really want to avoid a wrapper function (maybe you're running into a measurable running into perf issue), you can cast like so:

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

Or make it arguably more readable using a typedef for the compare function type:

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

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

Unfortunately, the straight C qsort() can't be typesafe, so it can't have have "error handling for when the arguments are not of type wchar_t". You, the programmer, are responsible for ensuring that you're passing the correct data, sizes and comparison function to qsort().


Edit:

To address some of the problems mentioned in other answers about the types being passed ot the compare function, here's a routine that can be used to sort wchar_t using the current locale's collating sequence. The library might have something better, but I'm not aware of it at the moment:

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

Also note, that the chararray you're passing to wcslen() isn't properly terminated - you'll need a 0 at the end of the initializer:

wchar_t chararray[] = {b, a, a1, 0};  
兮子 2024-09-21 09:37:23

您不能将函数指针转换为不同的类型,您当前的解决方案已经很好了

You can't cast a function pointer to a different type, your current solution is as good it gets

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