【问题标题】:C a comparator of int for an array of pointersC 一个指针数组的 int 比较器
【发布时间】:2012-10-02 07:15:29
【问题描述】:

我正在学习 LCTHW 教程,我有一个任务要做。 这是数据结构:

typedef struct DArray {
    int end;
    int max; 
    size_t element_size;
    size_t expand_rate;
    void **contents; 
} DArray;

我已经声明了一个 typedef:

typedef int (*DArray_compare) (const void *a, const void *b);

当我创建一个排序函数时,我将一个 DArray_compare 传递给它,问题是我无法弄清楚如何做这个比较器的示例。

我试图做这样的事情:

int compare(const void *a, const void *b)
{
  int i = (int)*a;
  int k = (int)*b;
  printf("%d %d\n", i, k);
  return i - k;
}

但我得到一个错误:

error: operand of type 'void' where arithmetic or pointer type is required int i = (int)*a;

问题是:在不改变比较器的struct和typedef的情况下,我想创建一个比较int的比较器,我该怎么做呢?

【问题讨论】:

  • 您需要稍微澄清一下上下文。比较器函数的参数到底是什么?它是数组中两个DArray 结构的地址,还是指向数组中两个结构的两个int 元素的指针?或者是其他东西?大多数答案都假设您正在传递两个指向整数的指针,因为这就是您的问题似乎表明正在发生的事情,但是通用排序函数将使用指向结构的指针。请注意,您的减法存在签名 int 溢出的风险,这会导致未定义的行为。
  • 比较器函数的参数是数组元素的指针。
  • 那么你的参数ab实际上是DArray指针,但你可以将它们视为指向第一个元素(end)的指针,使用各种符号,例如接受回答。

标签: c pointers compare function-pointers void-pointers


【解决方案1】:
int i = *(int*)a;
// This one has more parens to make it really obvious what your intent is.
int k = *((int*)b);

第二行 (k=) 最容易解释所有括号的 cos。你可以改写如下:

// Cast b from a pointer to a void into a pointer to an int.
int *X = (int*)b; 
// k = "what X is pointing to" or "the contents of X"
int k = *X;

编辑: 我认为 ralu 的评论建议您将所有 void* 更改为 int*,如果您有这种能力,这是一个更安全的解决方案。

typedef int (*DArray_compare) (const int *a, const int *b);

int compare(const int *a, const int *b)
{
    int i = *a;
    int k = *b;
    ...

【讨论】:

  • +1 ,他应该改变指针的类型而不仅仅是指向的值
  • int* 的问题是您不能将它与 qsort 和类似的标准函数一起使用。这似乎是 OP 正在做的事情。在这种情况下,参数必须为 void*。
  • 您的解决方案有效,但我不明白为什么,参数是指针,但是要访问它们的值,为什么我们必须这样做?
  • 谢谢,我以为不需要改变指针的类型来取它的内容。
【解决方案2】:

用于bsearch()qsort() 的标准C 库中DArray 结构数组的比较函数可能如下所示:

int compare(const void *a, const void *b)
{
    const DArray *d1 = a;
    const DArray *d2 = b;

    if (d1->end < d2->end)
        return -1;
    else if (d1->end > d2->end)
        return +1;
    else if (d1->max < d2->max)
        return -1;
    else if (d2->max > d2->max)
        return +1;
    else
        return 0;
}

显然,如果您需要比较其他字段,您可以很容易地将这些比较添加到上面的框架中。函数的一般结构是我推荐的编写此类比较器的方法。如果您愿意,您可以在分配行中添加显式强制转换; C++ 需要它们,但 C 不需要。

请注意,您的typedef 与比较器本身的相关性很小(尽管比较器作为函数指针应该与typedef 匹配)。它是比较器应具有的类型,但在编写函数时不能使用 typedef 名称。您可以在 sort 函数的实现及其声明中使用 typedef


我在几个地方观察到,作为比较器的结果返回两个有符号 int 值的差异会导致未定义的行为。

在对现已删除的答案的评论中,AR89 问道:

用 if 语句代替减法会更安全吗?

是的。考虑一下如果您有 16 位 int 值并比较 -30,000 和 +30,000 会发生什么;你有符号溢出,你可能会从比较器中得到一个正值,即使第一个值小于第二个。 32 位或 64 位整数可能会出现类似情况。它们相对不太可能;如果您知道您的值在范围内,那您就可以了。但是对于通用代码,您应该进行分段比较:

if (i < k)
    return -1;
else if (i > k)
    return +1;
else
    return 0;

不管ik 的值如何,它都能正常工作。另请注意,if 比较对于 unsigned int 类型也可靠,而减法实际上不起作用(结果始终为零或正数)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多