【问题标题】:Principle of different operand types adoption for ternary operator in CC中三元运算符采用不同操作数类型的原则
【发布时间】:2016-05-28 20:32:17
【问题描述】:

有多种类型转换可以确保赋值工作,例如隐式类型转换(提升)和显式类型转换(截断),但我不确定它如何在三元运算符的指针类型转换上工作。

#include <stdlib.h>

int main (void)
{
    (void)((rand() ? (char*)NULL :        NULL) + 1);
    /*     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        -> expression A */

    /* GCC Warning: pointer of type ‘void *’ used in arithmetic */
    (void)((rand() ? (char*)NULL : (void*)NULL) + 1);
    /*     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        -> expression B */

    return 0;
}

显然,编译器将表达式 A 视为 char* 的类型,而将 B 视为 void* 的类型。

我有两个问题:

  • 我检查了预处理后的代码,NULL 正好扩展为((void*)0),那为什么((void*)0)(void*)((void*)0) 是不同的类型呢?

  • 根据表达式B,为什么编译器将char*的类型转换为void*的类型,反之则不行?

【问题讨论】:

  • 在我看来,一个好的答案需要一个标准参考,但这里发生的是在第一种情况下将 NULL 隐式转换为 (char*)NULL,以及 (char*) 的隐式转换)NULL 到 (void*)NULL 在第二种情况下。不错的问题,再加上一个。

标签: c pointers casting null ternary-operator


【解决方案1】:

以下适用:

6.5.15 条件运算符

第一个操作数应为标量类型。
第二个和第三个操作数应满足以下条件之一:

/--/

——两个操作数都是指向合格或不合格版本的指针 兼容类型;
— 一个操作数是指针,另一个是空值 指针常量;或
— 一个操作数是指向对象类型的指针,并且 另一个是指向 void 的合格或不合格版本的指针。

在第一种情况下,您有一个操作数(char*)NULL,它是一个(非限定)类型指针,还有一个操作数NULL,它是一个空指针常量。

在第 6 节进一步说:

如果第二个和第三个操作数都是指针或一个为空 指针常量,另一个是指针,结果类型是 指向使用类型的所有类型限定符限定的类型 被两个操作数引用

简而言之:如果一个操作数是char* 类型,而另一个是空指针常量,则结果是char*

在第二种情况下,您有一个指向类型的指针和一个void*。空指针常量也不是(我将在下面进一步解释原因)。在它说的同一段中进一步向下(强调我的):

...如果一个操作数是 空指针常量,结果具有另一个操作数的类型; 否则,一个操作数 是指向 void 或 void 的限定版本的指针,在这种情况下,结果类型是 指向适当限定版本的 void 的指针。

意味着第二个?: 操作的结果是void* 类型。这应该回答你的第二个问题。


回答你的第一个问题,它与 ?: 这样的运算符无关,而是关于空指针与空指针常量的“古老的 C 之谜”。

宏 NULL 的扩展方式是实现定义的,它是 0(void*)。它保证是一个空指针常量。

There is a difference between a null pointer and a null pointer constant,即空指针可以有任何实现定义的值,而空指针常量总是0(void*)0

6.3.2.2 说:

值为 0 的整数常量表达式,或这样的表达式 强制转换为 void * 类型称为空指针常量。如果一个空 指针常量转换为指针类型,结果 指针,称为空指针,保证比较不等于 指向任何对象或函数的指针。

所以(void*)NULL 是一个空指针,但它不是一个空指针常量。因此,条件运算符将其视为 void 指针。

总结一下:空指针不是条件运算符的特例,只有空指针常量才是。

【讨论】:

  • +one 现在很有意义。
【解决方案2】:

我会尝试解释:

0(void*)0 是一个空指针常量,由三元条件运算符适当处理:

如果一个操作数是空指针常量,则结果的类型为 另一个操作数的; (6.5.15 6)

但是(void*)((void *)0)是一个空指针,而不是空指针常量(NULL):

值为 0 的整数常量表达式,或这样的表达式 强制转换为 void * 类型称为空指针常量。如果一个空 指针常量转换为指针类型,结果 指针,称为空指针,保证比较不等于 指向任何对象或函数的指针。 (6.3.2.3 3)

因此,本段适用:

否则,一个操作数是指向 void 的指针或 void,在这种情况下,结果类型是指向适当的指针 无效的合格版本。 (6.5.15 6)

【讨论】:

    猜你喜欢
    • 2012-04-02
    • 1970-01-01
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 2018-10-12
    • 2015-07-02
    • 2020-08-16
    • 1970-01-01
    相关资源
    最近更新 更多