C中三元运算符采用不同操作数类型的原理
Principle of different operand types adoption for ternary operator in C
有很多种类型转换来确保赋值工作,例如隐式类型转换(提升)和显式类型转换(截断),但我不确定它如何在三元运算符的指针类型转换上工作。
#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*
的类型,而不是相反?
我会尝试解释一下:
0
或 (void*)0
是空指针常量,由三元条件运算符适当处理:
if one operand is a null pointer constant, the result has the type
of the other operand; (6.5.15 6)
但是(void*)((void *)0)
是一个空指针,而不是空指针常量(NULL
):
An integer constant expression with the value 0, or such an expression
cast to type void *,is called a null pointer constant. If a null
pointer constant is converted to a pointer type, the resulting
pointer, called a null pointer, is guaranteed to compare unequal to a
pointer to any object or function. (6.3.2.3 3)
因此,此段现在适用:
otherwise, one operand is a pointer to void or a qualified version of
void, in which case the result type is a pointer to an appropriately
qualified version of void. (6.5.15 6)
以下适用:
6.5.15 Conditional operator
The first operand shall have scalar type.
One of the following shall hold for the second and third operands:
/--/
— both operands are pointers to qualified or unqualified versions of
compatible types;
— one operand is a pointer and the other is a null
pointer constant; or
— one operand is a pointer to an object type and
the other is a pointer to a qualified or unqualified version of void.
在第一种情况下,您有一个操作数 (char*)NULL
,它是一个(非限定的)类型指针,还有一个操作数 NULL
,它是一个空指针常量。
在 §6 中更进一步说:
If both the second and third operands are pointers or one is a null
pointer constant and the other is a pointer, the result type is a
pointer to a type qualified with all the type qualifiers of the types
referenced by both operands
用简单的英语来说:如果一个操作数是 char*
类型,另一个是空指针常量,则结果是 char*
.
在第二种情况下,您有一个指向类型的指针和一个 void*
。空指针常量也不是(我将在下面进一步解释原因)。在同一段落的下方,它说(强调我的):
... if one operand is a
null pointer constant, the result has the type of the other operand; otherwise, one operand
is a pointer to void or a qualified version of void, in which case the result type is a
pointer to an appropriately qualified version of void.
表示第二个?:
操作的结果是void*
类型。这应该回答你的第二个问题。
要回答你的第一个问题,它与 ?:
运算符本身无关,而是关于空指针与空指针常量的 "ancient C mystery"。
如何扩展 NULL 宏是实现定义的,它是 0
或 (void*)
。它保证是一个空指针常量。
,即空指针可以具有任何实现定义的值,而空指针常量始终是 0
或 (void*)0
.
6.3.2.2 说:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant. If a null
pointer constant is converted to a pointer type, the resulting
pointer, called a null pointer, is guaranteed to compare unequal to a
pointer to any object or function.
所以(void*)NULL
是空指针,但不是空指针常量。因此,条件运算符将其视为空指针。
总结一下:空指针不是条件运算符的特例,只有空指针常量才是。
有很多种类型转换来确保赋值工作,例如隐式类型转换(提升)和显式类型转换(截断),但我不确定它如何在三元运算符的指针类型转换上工作。
#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*
的类型,而不是相反?
我会尝试解释一下:
0
或 (void*)0
是空指针常量,由三元条件运算符适当处理:
if one operand is a null pointer constant, the result has the type of the other operand; (6.5.15 6)
但是(void*)((void *)0)
是一个空指针,而不是空指针常量(NULL
):
An integer constant expression with the value 0, or such an expression cast to type void *,is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function. (6.3.2.3 3)
因此,此段现在适用:
otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void. (6.5.15 6)
以下适用:
6.5.15 Conditional operator
The first operand shall have scalar type.
One of the following shall hold for the second and third operands:/--/
— both operands are pointers to qualified or unqualified versions of compatible types;
— one operand is a pointer and the other is a null pointer constant; or
— one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void.
在第一种情况下,您有一个操作数 (char*)NULL
,它是一个(非限定的)类型指针,还有一个操作数 NULL
,它是一个空指针常量。
在 §6 中更进一步说:
If both the second and third operands are pointers or one is a null pointer constant and the other is a pointer, the result type is a pointer to a type qualified with all the type qualifiers of the types referenced by both operands
用简单的英语来说:如果一个操作数是 char*
类型,另一个是空指针常量,则结果是 char*
.
在第二种情况下,您有一个指向类型的指针和一个 void*
。空指针常量也不是(我将在下面进一步解释原因)。在同一段落的下方,它说(强调我的):
... if one operand is a null pointer constant, the result has the type of the other operand; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void.
表示第二个?:
操作的结果是void*
类型。这应该回答你的第二个问题。
要回答你的第一个问题,它与 ?:
运算符本身无关,而是关于空指针与空指针常量的 "ancient C mystery"。
如何扩展 NULL 宏是实现定义的,它是 0
或 (void*)
。它保证是一个空指针常量。
0
或 (void*)0
.
6.3.2.2 说:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
所以(void*)NULL
是空指针,但不是空指针常量。因此,条件运算符将其视为空指针。
总结一下:空指针不是条件运算符的特例,只有空指针常量才是。