这个 C 语法是啥意思?

Posted

技术标签:

【中文标题】这个 C 语法是啥意思?【英文标题】:What does this C syntax mean?这个 C 语法是什么意思? 【发布时间】:2011-11-13 07:57:48 【问题描述】:

这是来自我正在使用的“魔术”数组库。

void
sort(magic_list *l, int (*compare)(const void **a, const void **b))

    qsort(l->list, l->num_used, sizeof(void*),
         (int (*)(const void *,const void *))compare);

我的问题是:qsort 的最后一个参数到底在做什么?

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

qsort 采用 int (*comp_fn)(const void *,const void *) 作为比较器参数,但此排序函数采用双指针比较器。不知何故,上面的行将双指针版本转换为单指针版本。有人可以帮忙解释一下吗?

【问题讨论】:

C 语法的意思是undefined behavior。 这到底是怎么回事?如果向我展示了该 sort 函数的原型并要求为它编写一个比较函数,我会将参数转换为 int ** 并双重取消引用它们以获得该值,这很可能会崩溃该程序。或者给出不正确的结果。 有些东西看起来很奇怪。 compare 函数可能最终会执行 (**a > **b) 但 qsort 将仅使用指向元素的指针调用 compare。所以它可能会取消引用它一次太多。或者数组中的元素可能是指针。 sort 是对指针进行排序。在这种情况下,typedef 会很好。 【参考方案1】:

qsort 的最后一个参数是将一个采用双指针的函数指针转换为一个采用单指针的 qsort 将接受的指针。这只是一个演员表。

【讨论】:

虽然看起来很奇怪;为什么不在sort 的声明中加入呢? @Tom 因为 sort 的声明中的那个允许您更改指针,以便您可以在演员阵容中使用它们,这就是它们是双指针的原因 @Tom 在 sort 的声明中强制转换它会给函数的用户带来关于强制转换的警告的负担。托尼,好点子,我忽略了使用 & 取消引用的(不)方便 我猜想强制转换与 sort 使用 qsort 作为实现细节这一事实有关。如果它在 sort 的声明中使用单指针版本,那么它的消费者必须是在调用它时进行强制转换的人。 @Tom_Zych,因为调用者可能正在使用不同类型的 args(sort() 中的当前 arg 类型),如果直接传递给 qsort() 将产生警告/错误。这意味着用演员表修复每个来电者。这意味着丑陋的代码。也许 compare() 函数是遗留代码的一部分。或来电者太多。【参考方案2】:

(int (*)(const void *,const void *))compare 是一种 C 风格转换,用于将函数指针 compare 转换为具有两个 const void * 参数的函数指针。

【讨论】:

【参考方案3】:

它正在转换一个函数指针。我想这是因为比较可以应用于被取消引用的指针,而不是它们指向的任何指针。

【讨论】:

【参考方案4】:

最后一个参数是一个函数指针。它指定它接受一个指向函数的指针,该函数返回一个 int 并接受两个 const void ** 参数。

【讨论】:

【参考方案5】:

这正是你引用的演员所做的:它转换类型的指针

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

指向类型的指针

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

后者是qsort所期望的。

这样的事情在质量差的代码中经常遇到。例如,当某人想要对ints 的数组进行排序时,他们通常会编写一个接受指向int * 的指针的比较函数

int compare_ints(const int *a, const int *b) 
  return (*a > *b) - (*a < *b);

当真正调用 qsort 时,他们会强制将其转换为正确的类型以抑制编译器的抱怨

qsort(array, n, sizeof *array, (int (*)(const void *,const void *)) compare_ints);

这是一种“hack”,会导致未定义的行为。显然,这是一种不好的做法。您在示例中看到的只是相同“hack”的不太直接的版本。

在这种情况下,正确的方法是将比较函数声明为

int compare_ints(const void *a, const void *b) 
  int a = *(const int *) a;
  int b = *(const int *) b;
  return (a > b) - (a < b);

然后在没有任何强制转换的情况下使用它

qsort(array, n, sizeof *array, compare_ints);

一般来说,如果希望将它们的比较函数用作qsort(和类似函数)中的比较器,则应使用const void * 参数来实现它们。

【讨论】:

你能多评论一下为什么会产生未定义的行为吗?听起来我可能错过了这里的一个微妙之处,这使得包括我的答案在内的答案不准确。 这不是 C 中未定义的行为,是吗?它是用 C++ 编写的吗?我有兴趣,你能解释一下吗? @K-ballo:C 中的 未定义行为。int(const void*, const void*) 类型与 int(const int*, const int*) 类型不兼容,这意味着通过强制转换的指针调用函数会导致未定义的行为。 @Tommy:仅当函数类型兼容时,才允许将一种函数指针类型转换为另一种,然后通过转换后的值调用函数。兼容性的完整定义在 6.7.5.3/15 中给出。并且上述类型不兼容,因为const int *const void * 不兼容。 恕我直言,它没有任何问题。该程序想要对一个指针数组进行排序。转换可以放在回调函数中:(a void*) 可以转换为 any 指针,包括 (void**)。这或多或少是一个成员函数,包含 std 库和 magic_list 事物内部的东西之间的粘合逻辑(包括强制转换)。【参考方案6】:

在大多数硬件上,您可以假设指针在硬件级别上看起来都一样。例如,在具有平面 64 位寻址指针的系统中,指针始终是 64 位整数。指向指针的指针或指向指针的指针也是如此。

因此,用于调用具有两个指针的函数的任何方法都适用于具有两个指针的任何函数。指针的具体类型无关紧要。

qsort 一般地对待指针,就好像每个指针都是不透明的。所以它不知道也不关心它们是如何被取消引用的。它知道它们当前所处的顺序,并使用 compare 参数来计算它们应该处于什么顺序。

您正在使用的库可能会保存指向指针的指针列表。它有一个比较函数,可以将两个指针与指针进行比较。所以它把它传递给qsort。它只是在语法上比例如更好。

qsort(l->list, l->num_used, sizeof(void*), compare);

/* elsewhere */

int compare(const void *ptr1, const void *ptr2)

    // these are really pointers to pointers, so cast them across
    const void **real_ptr1 = (const void **)ptr1;
    const void **real_ptr2 = (const void **)ptr2;

    // do whatever with real_ptr1 and 2 here, e.g.
    return (*real_ptr2)->sort_key - (*real_ptr1)->sort_key;

【讨论】:

在某些特定平台上,有很多事情发生在“硬件级别看起来相同”。然而,总的来说,这是不正确的。最重要的是,该语言并不关心“在硬件级别上看起来相同”的东西。语言清楚地表明这是导致未定义行为的黑客攻击。 语言规范没有说明任何类型的东西——它不是语言规范对代码进行价值判断的地方。如果 C 规范明确指出这是“导致未定义行为的 hack”,那么 C 规范将明确排除任何人创建该语言的严格超集的可能性。 "这不是语言规范对代码进行价值判断的地方" 是的,它是!语言规范旨在将语言实现(以及这些实现的具体细节)分为两类:符合(或“好”)和不符合(或“坏”) ”)。不一致的代码有时很有用(当您可以依赖该特定实现时),但一般是不好的,不应该推荐。无论如何,-1 表示“指针的具体类型无关紧要”。很明显。 不,语言规范定义了一些东西,而其他的东西未定义,无论是隐式的还是显式的。例如,这允许 Objective-C 成为 C 的严格超集——它遵循所有 C 规则,并在 C 未定义的地方添加了一些新想法。但根据您的说法,这意味着每次有人编写一个 Objective-C 方法调用时,这都是 C99 规范的一种破解,而不仅仅是它没有定义的概念。 @Tommy:当有人依赖 C 程序中未定义行为的特定表现时,就会发生黑客攻击。所以,这是两部分陈述的折叠形式: 1. 语言说它是 UB。 2 在代码中依赖特定形式的 UB 是一种 hack。这样,在原始代码的上下文中,人们可以将“语言”与“黑客”划清界限。这就是我的意思,不多不少。

以上是关于这个 C 语法是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

C ++结构语法“a:b”是啥意思

*[ ... ] 语法是啥意思? [复制]

这个语法在ruby中是啥意思[重复]

这个语法是啥意思? $node->option [重复]

(Python)这个函数中的语法“slider.var”是啥意思

buffer 在c语言中是啥?