【问题标题】:Ternary Operator (?:) based assignment avoids type check in C基于三元运算符 (?:) 的赋值避免了 C 中的类型检查
【发布时间】:2022-01-19 19:52:57
【问题描述】:

我无法理解为什么编译器在通过 (?:) 分配不兼容的指针时不发出警告,但在直接分配时却发出警告。

编译器在这种情况下发出警告:

test.c:在函数“main”中:test.c:8:4:警告:分配给 'uint32_t *' {aka 'unsigned int *'} 来自不兼容的指针类型 'uint32_t **' {aka 'unsigned int **'} [-Wincompatible-pointer-types] 8 |一个=数组; | ^

#include <stdint.h>
#include <stdlib.h>

int main(void)
{
    uint32_t *array[10], *a;
    a = array;
}

以下情况无警告:

#include <stdint.h>
#include <stdlib.h>

int main(void)
{
    int b = 8;
    uint32_t *array[10], *a;

    a = (b >= 8) ? array : malloc(8);
}

环境:

Gcc version 9.3.0
Ubuntu 20.04
compilation cmd: gcc test.c -o test.out

【问题讨论】:

  • 提示:malloc(8) 的类型是什么? (b &gt;= 8) ? array : malloc(8)是什么类型的?

标签: c gcc implicit-conversion conditional-operator void-pointers


【解决方案1】:

表达式(b &gt;= 8) ? array : malloc(8) 的类型是void*(因为malloc(8) 的类型是void*)。你可以通过做一些无意义的事情并让编译器告诉你来看到这一点:

((b >= 8) ? array : malloc(8)) * 5;
<source>:10:36: error: invalid operands to binary * (have 'void *' and 'int')
   10 |     ((b >= 8) ? array : malloc(8)) * 5;
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
      |                       |
      |                       void *

void* 可以隐式转换为任何类型的指针,这就是当您将该值分配给a 时编译器不会报错的原因。

【讨论】:

  • 更准确地说:三元运算符的一个特定规则是允许第二个和第三个操作数之一是指向void 的合格或不合格指针,另一个是拥有任何对象指针类型,在这种情况下,结果是一个适当限定的指向void 的指针。这与malloc 无关,也与第三个操作数vs 第二个有关。
  • 另外,FWIW,我认为这条规则是错误的。它应该走另一条路,正是因为问题中的这种情况。
  • @JohnBollinger 反过来说,你认为三元表达式的类型应该是非void*类型?
  • 是的,@Kevin,这将是 IMO 更好的规则。
  • 我会走得更远,并要求第二个和第三个表达式的类型完全匹配,限定符和所有。因此,例如,必须将 NULL 指针或 void 指针强制转换为适当限定的指针类型,否则会产生错误。
【解决方案2】:

来自 C 标准(6.5.15 条件运算符)

  1. ... 否则,一个操作数是指向 void 的指针或 void 的限定版本,在这种情况下,结果类型是指向 void 的适当限定版本。

函数malloc 返回void * 类型的指针。所以带有条件运算符的表达式的类型是void *void * 类型的指针可以分配给任何其他对象类型的指针。

来自 C 标准(6.3.2.3 指针)

1 指向 void 的指针可以转换为指向任何 对象类型。指向任何对象类型的指针都可以转换为 指向 void 并再次返回的指针;结果应比较等于 原始指针。

其实你有

a = ( void * )(b >= 8 ? array : malloc(8) );

【讨论】:

  • 感谢您的回答。它肯定回答了这个问题,但我认为不需要它的第二部分。这里的目的是编写代码以避免发生不兼容的指针分配而没有编译器警告的情况。 (void *) 演员表并不是真正的方法。您可以删除它还是提供解决方案。完成后,我可以将此答案标记为已接受。
  • @Rajnesh 所谓的第二部分描述了为什么没有对三元运算符的警告,因为 void * 类型的指针可能被分配给指向任何其他类型的对象指针。这部分很重要。
  • 好的,现在知道了。对不起这是我的错。我误会了。
  • @Rajnesh 完全没有。不客气。:)
猜你喜欢
  • 2019-05-24
  • 2021-04-29
  • 2015-08-29
  • 2013-08-13
  • 2013-06-28
  • 2021-11-23
  • 2014-05-24
  • 1970-01-01
  • 2017-01-20
相关资源
最近更新 更多