C语言中三元运算符采用不同操作数类型的原理

时间:2016-02-17 08:39:01

标签: c pointers casting null ternary-operator

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

#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*的类型而不是反之亦然?

2 个答案:

答案 0 :(得分:1)

我会尝试解释:

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

  

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

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

  

值为0的整型常量表达式,或此类表达式   强制转换为void *,称为空指针常量。如果为null   指针常量被转换为指针类型,由此产生   指针,称为空指针,保证比较不等于a   指向任何对象或函数的指针。 (6.3.2.3 3)

因此,现在,本段适用:

  

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

答案 1 :(得分:1)

以下适用:

  

6.5.15条件运算符

     

第一个操作数应具有标量类型   下列之一应适用于第二和第三个操作数:

     

/ - /

     

- 两个操作数都是指向合格或非限定版本的指针   兼容类型;
   - 一个操作数是指针,另一个操作数是空   指针常数;或
   - 一个操作数是指向对象类型的指针   另一个是指向合格或不合格版本的void的指针。

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

在§6中进一步说,它说:

  

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

简单来说:如果一个操作数的类型为char*而另一个操作数是空指针常量,则结果为char*

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

  

...如果一个操作数是a   null指针常量,结果具有另一个操作数的类型; 否则,一个操作数   是指向void或void的限定版本的指针,在这种情况下,结果类型是a   指向适当合格版本的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 *,称为空指针常量。如果为null   指针常量被转换为指针类型,由此产生   指针,称为空指针,保证比较不等于a   指向任何对象或函数的指针。

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

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