【问题标题】:"Incompatible pointer type" compiler warning for 4th argument of qsortqsort 的第四个参数的“不兼容的指针类型”编译器警告
【发布时间】:2011-03-30 13:29:44
【问题描述】:

我正在尝试使用标准库的 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 * 类型时,它看起来应该有错误处理。

【问题讨论】:

    标签: c sorting pointers casting widechar


    【解决方案1】:

    您的做法非常正确。The gcc documentation for strcoll and wcscoll 给出了一个与此类似的示例,作为将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来解决它@。

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

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

    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 对字符进行排序。这是一个用于对 strings 进行排序的函数(在 C 中是指向以 nul 结尾的字符序列的指针)。以上是假设您尝试对字符串进行排序的情况下编写的。如果你想对字符进行排序,那么wcscoll 不是合适的函数,但上面关于qsort 的所有内容仍然适用。

    【讨论】:

    • 很好地抓住了这个错误,但是看起来 OP 想要对字符串中的字符进行排序,在这种情况下,他得到了指向要排序的元素的正确指针,但 wcscoll() 不是t 调用正确的函数,除非字符被移动到本地、一个字符长度、以空结尾的字符串(在这种情况下,包装函数几乎是强制性的)。是否有标准的 lib 函数用于使用语言环境的整理序列比较单个 char/wchar_t 值?
    • 是的,我(她,不是他,顺便说一句)想在对整个字符串进行排序之前开始对单个字符进行排序。我正在尝试处理 wchar_t。
    • 非常感谢。我还在努力消化这个,非常有帮助。最初的错误是认为 wcscoll 对字符进行排序。
    【解决方案2】:

    有两个问题:您混淆了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 通常需要一个简单的包装器。

    【讨论】:

    • 这是一个非常简洁的解释。非常感谢。我将把这个标记为正确的,因为它可能对未来的读者更有用,尽管我个人从 Tyler McHenry 那里学到了更多,他详细讨论了代码。因此,未来的读者请注意:如果您遇到相同的问题,两者都值得您花时间。
    【解决方案3】:

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

    【讨论】:

    • 你好迈克尔;您写道“您可以通过将传递给 qsort() 的函数指针转换为适当的类型来删除包装函数”。在许多 C 常见问题解答中强烈建议不要这样做,通常是在“我正在尝试将函数 strcmp 传递给 qsort”的问题中。这个答案总是使用包装器。基本原理可能是 ABI 可能会根据其类型的细微差异以不同的方式传递参数。您可以从一个函数指针类型转换为另一个类型,然后再转换回原始类型,但这是函数指针转换的唯一合法用途。
    • @Pascal:我同意。我应该加强我的回答对此的评论。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2015-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-27
      • 2011-08-17
      相关资源
      最近更新 更多