【问题标题】:Confused about Qsort and Pointers对 Qsort 和指针感到困惑
【发布时间】:2013-10-29 22:24:08
【问题描述】:

我是一名 C 初学者,想习惯术语和指针。 在寻找对数值数组的元素进行排序的方法时,我发现了以下工作函数原型。该函数是 qsort,它使用指针。现在我的理解是“const”这个词确保值 a 和 b 不变,但指针不变。如果我在这里错了,请纠正我。我的问题是:

  1. 为什么我们使用void *这个函数我们不能使用int *从 开始?
  2. 返回部分*(int*)a如何构造 工作?
  3. 为什么 qsort 算法需要这么多参数?

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

非常感谢您的回答。 PS:这对我来说是一项相当复杂的任务。

【问题讨论】:

    标签: c pointers qsort


    【解决方案1】:
    1. qsort 是这样制作的,因此它可以用作通用分类器。如果它从一开始就使用int,它只能用于比较整数。例如,通过这种方式,您还可以通过将 strcmp 作为比较函数传递给 qsort 来对字符串进行排序。
    2. *(int*)aa 转换为指向int 的指针,然后取消引用它,因此您将获得存储在 a 的整数。请注意,这不会更改 aa 指向的值。
    3. qsort 需要 4 个参数:要排序的数组、该数组中元素的数量和元素的大小,最后是比较函数。它需要所有这些信息,因为它再次被尽可能地通用。

      它需要元素的数量,因为在 C 中指针不携带有关其后缓冲区大小的信息。它需要知道每个元素的大小,以便正确地将元素传递给比较函数。例如,要比较ints,您将传递sizeof(int) 作为大小参数。要比较字符串,您可以使用 sizeof(char *)

    ADDIT 正如 H2CO3 所建议的那样,使用const void * 的原因是表明比较功能可能不会改变ab 所指向的值。当然,这是为了确保对数组进行排序不会突然改变数组中的值。而且,正如 H2CO3 所说,转换为 (const int *) 会更干净,这样您就不会在转换后意外更改值:

    return *(const int *)a - *(const int *)b;
    

    您也可以通过以下方式摆脱演员阵容:

    int compare(const void * a, const void * b){
        const int * ia = a;
        const int * ib = b;
    
        return *ia - *ib;
    }
    

    取决于您对演员表的喜好。 (我宁愿避开它们)

    最后,澄清一下星号:

    *(int *)a
    ^     ^
    |     └ cast to integer pointer
    └ dereference (integer) pointer
    

    【讨论】:

    • +1 这真的很有帮助。不过,有必要澄清 const void * 中的 const 限定符的含义(还包括一个建议,即转换为 const int * 会更合适。)
    • 第二个星号是尊重它吗?
    • @Vesnog *(int*)a 中的第二个星号将转换为 int *: 987654348@)。然后第一个星号取消引用 int * 以获得 int
    • 哦,好吧,它是正确的,但引用第一个星号,就好像它是第二个。然后第二个星号 - 在您的定义中 - 将指针的类型从 void 更改为 int。不幸的是,@H2CO3 在参数部分输入 int 而不是 void 不起作用。
    • @Vesnog 我根本不建议在参数中输入 int 而不是 void 。仔细阅读我的评论。
    【解决方案2】:

    现在我的理解是“const”这个词确保值ab保持不变,但指针不变

    你理解错了。

     const int *a;  
    

    a 声明为指向常量int 类型的指针。这意味着const这个词确保你不能通过修改*a来修改a指向的变量的值。

    为什么我们使用void *这个函数不能从一开始就使用int *?

    void * 用于指向任何类型的变量。

    return 部分中的构造*(int*)a 是如何工作的?

    *(int *) 用于将a 转换为指向int 的指针,然后解除对它的引用以获取存储在它指向的位置的值。

    【讨论】:

    • +1 第一个误解在其他答案中没有得到解决。
    【解决方案3】:

    其他答案都很好。我只想补充一点,如果你对回调函数非常清楚,它通常更容易阅读。

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

    变成

    int compare (const void *a, const void *b)
    {
        int ia = *(int *)a;
        int ib = *(int *)b;
        return ia - ib;
    }
    

    在这种情况下,它并不太重要,但随着比较函数变得复杂,您可能希望在进行比较之前将变量设置为“您的类型”。

    既然你在下面的评论中问过,这里是一个非常循序渐进的版本:

    int compare (const void *a, const void *b)
    {
        int *pa = (int *)a;
        int *pb = (int *)b;
        int ia = *pa;
        int ib = *pb;
        return ia - ib;
    }
    

    【讨论】:

    • 所以表达式 *(int *)a 中的第二个 * 执行了获取 a 值的取消引用?
    • (int *)a 部分将 a 从 void * 转换为 int *。 (int *) 左侧的 * 将 int * 取消引用到 int。
    • 好的,没错。顺便说一句,输入计算机术语是什么意思?谢谢
    • “to cast”一般是指将一种类型变为另一种类型。
    • 好的,现在一切似乎都清楚了;感谢您的努力。
    【解决方案4】:

    qsort() 函数是作为通用例程实现的通用算法 的示例。这个想法是让它对排序任意对象有用,而不仅仅是intfloat。因此(也因为 C 语言设计),qsort() 采用比较函数作为接受两个通用(在 C 意义上)指针的参数。由该函数(由qsort() 用户提供)将这些指针转换为正确的类型、执行正确的比较并返回排序指示。

    同样,由于qsort() 事先不知道有多大 对象,因此它将对象大小作为参数。就qsort() 而言,对象是在内存中连续排列的大小相等的字节块。

    最后,由于qsort() 执行的任何操作都不会导致错误,因此它不会返回错误代码。实际上qsort()可能失败的情况,这是传递给它的非法参数,但在许多其他标准C库例程的传统中,它不保证对承诺未定义的参数进行任何错误检查这种情况下的行为。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-11
      • 2013-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多