【问题标题】:function pointers with void* argument casting带有 void* 参数转换的函数指针
【发布时间】:2021-05-09 10:57:21
【问题描述】:

我不知道如何查找它,即使标题令人困惑,即使我对我要查找的内容也感到困惑,而且这个问题肯定已经被问过,但它是如此具体,所以这里有一点上下文:

int comparison(const int* a, const int* b) {
    return *a - *b;
}

int main(int argc, char const *argv[])
{
    int arr[3] = {1,6,-2};
    qsort(arr,3,sizeof(int),comparison);
    return 0;
}

好吧,它确实有效,但是编译器给了我一个警告,因为 qsort 想要一个类型的函数:

int(*)(const void*, const void*) 

比较是类型的函数:

int(*)(const int*, const int*) 

我想知道为什么编译器不满意,因为它只需要转换地址。甚至应该很高兴为 void* 指针提供类型。这真的很糟糕吗?像未定义的行为或什么?还是只是编译器在抱怨什么?

【问题讨论】:

标签: c++ c pointers


【解决方案1】:

在已经给出了其他原因之后,还有另一个原因。从历史上看,void *int * 有不同的位排列的平台,我听说过一个 void *int * 大小不同的平台。该函数指针转换并不总是有效。

  const int *ia = (const int *)a;
  const int *ib = (const int *)b;

可能不会编译成ia = a;,而是像ia = a >> 1这样的东西;所以真的必须有一个地方来存放这些说明。

【讨论】:

    【解决方案2】:

    为什么他(编译器)不高兴。

    qsort() 需要 int (*)(const void *a, const void *b) 类型的功能点,而不是 int (*)(const int *a, const int *b)。编译器可以猜测它的 OK 并进行强制转换,但编译器对此类问题发出警告会更有效率。

    或者只是编译器抱怨什么?

    通过温暖你,你可以确定问题的程度。


    除了@Alex Reynolds好的答案,注意*a - *b可能会溢出,导致比较错误。

    改为:

    int comparison(const void *a, const void *b) {
      const int *ia = (const int *)a;
      const int *ib = (const int *)b;
      return (*ia > *ab) - (*ia < *ib);
    }
    

    优秀的编译器能够识别(p&gt;q) - (p&lt;q) 习语并生成高效代码。

    【讨论】:

    • 谢谢你,我现在明白了,我会记住你的比较技巧。
    【解决方案3】:

    qsort 函数将您未传递的类型的函数作为其参数之一。所以你需要改变它。

    在比较器内部,您可以将指针重新转换为所需的类型。

    此外,您需要 dereference 将要传递给比较器函数的 const int 指针的值:

    #include <stdio.h>
    #include <stdlib.h>
    
    static int 
    comparator(const void *a, const void *b) 
    {
        return *(const int *)a - *(const int *)b;
    }
    
    static void
    printArr(int arr[], int n) 
    { 
        int i; 
        for (i = 0; i < n; ++i) {
            printf("%d ", arr[i]);
        } 
    } 
    
    int
    main(int argc, const char **argv)
    {
        int arr[3] = {1, 6, -2};
        qsort(arr, 3, sizeof(int), comparator);
        printArr(arr, 3);
    
        return EXIT_SUCCESS;
    }
    

    【讨论】:

    • 感谢您的回复,但我想我还不够清楚:我知道问题是什么,我想知道为什么会出现问题。为什么编译器不能接受我想直接将 void* 转换为 int* ,而不必在函数中进行转换。
    • 请注意,如果您有足够大的不同符号值进行比较,则比较器函数中的减法存在溢出风险。将正文设置为:{ int va = *(const int *)a; int vb = *(const int *)b; return (va &gt; vb) - (va &lt; vb); }{ int va = *(const int *)a; int vb = *(const int *)b; if (a &lt; b) return -1; else if (a &gt; b) return +1; else return 0; } 可能会更好。
    • 没有什么比qsort 函数被定义为接受该类型的函数更复杂的了,所以这就是你必须传递给它的东西。您可以编写自己的排序函数来接受自定义类型的比较器,尽管您失去了通用 qsort 已经提供的很多灵活性。假设有一天您决定将数组从存储 int 更改为存储 long 值(无论出于何种原因)。使用通用 qsort 更改的代码要少得多。
    猜你喜欢
    • 2014-09-05
    • 2019-04-13
    • 1970-01-01
    • 2018-08-25
    • 2011-07-31
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2013-05-21
    相关资源
    最近更新 更多