C11 对 void 表达式有如下描述:
6.3.2.2
void
void 表达式(类型为
的表达式)的(不存在的)值不得以任何方式使用,并且隐式或显式转换(除了void
)不得应用于此类表达式。如果任何其他类型的表达式被计算为 void 表达式,则其值或指示符将被丢弃。 (空表达式会评估其副作用。)void
我读过很多人对这段话的解释。现在,我大概明白了丢弃其价值意味着什么。然而,这段话中的“指示符被丢弃”让我很困惑。
“指示符被丢弃”是什么意思?这里的“指定者”指的是什么?能举个例子给我看一下吗?
这个问题可能很简单,也许根本没有用。但我还是想知道这里标准的用意。
想象一下:
void foo(int a)
{
(void)a;
/* .... */
}
https://godbolt.org/z/7845nxKzh
该值和指示符被丢弃,但如果容易产生副作用,则可能会对其进行评估。
示例:
volatile x;
(void)x;
C 标准使用“指示符”一词来指代三种事物,唯一适用于此的是函数指示符。
函数的名称是函数指示符,将一元
*
应用于指向函数的指针的结果是函数指示符。
表达式被计算为 void 表达式的地方是:
,
运算符的左操作数。for
语句括号中的第三部分。C 语义中没有理由在这些地方使用函数指示符,也没有任何语义理由使用应用了
*
的函数指针而不是仅仅使用函数指针。 (函数指针表达式可能包含副作用,因此如果不存在,代码在语义上可能会有所不同。)委员会可能指定了当函数指示符被评估为 void 表达式以确保完整性时会发生什么,我们可以想象可能会出现剩余的函数指示符从各种预处理器结构中。 (例如,当预处理器定义选择各种特征时,宏会被替换为各种所需的表达式,而当未选择特征时,宏会被某些默认的“我们不关心这个”代码替换,这些代码恰好是功能指示符。)
有争议的是,6.3.2.2 中不需要包含指示符,因为 void 表达式从不包含函数指示符,因为函数指示符会自动转换为指针,而被丢弃的是指针。 C 2018 6.3.2.1 4 表示函数指示符会自动转换为指针,除非它是
sizeof
或一元 &
的操作数。因此,例如,在语句表达式main;
中,main
不是sizeof
或一元&
的操作数,因此它被转换为指向main
的指针,然后丢弃该指针。
(标准使用“指示符”一词的另外两件事是初始值设定项中的指示符,例如
.foo
中的 struct bar x = { .foo = 3 };
,以及 offsetof
宏中的成员指示符。这些都不是可能出现的东西作为一个空表达式。)
C 标准区分值和函数指示符,它们不称为值:
6.3.2.1 左值、数组和函数指示符
[...] 函数指示符是具有函数类型的表达式。除非它是
运算符、sizeof
运算符或一元typeof
运算符的操作数,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回的指针”的表达式类型”。&
考虑到接近性(6.3.2.1和6.3.2.2),引用段落中的指示符可能指的是功能指示符。
计算结果为函数指示符的表达式(例如:函数名称 (
main
) 或对 signal
函数的调用(技术上返回指向函数的 pointer),未存储到传递给另一个函数或用作初始化程序的对象会被简单地丢弃,即:不使用。