为什么这段代码能编译通过:
int main()
{
{}
}
但这不会:
{}
int main()
{
}
第一种情况,你正在函数内定义一个块,这是允许的(它可以限制能见度)。 第二种情况,你正在定义一个匿名块,这是不允许的(它需要在函数定义之前,否则编译器将永远不知道何时执行它)
{}
是一个无操作语句(在C语法中它是一个空的复合语句)。您可以在函数中放置语句,但不能在其他地方放置语句。
我想标准没有禁止在您的第一个示例中使用空语句的原因是虽然它是无意义的,但它没有害处,并且为何要引入允许花括号为空时的规则只会增加复杂度而没有任何益处。
严谨地说,语法也没有定义文件范围的任何其他构造,其中 {}
是有效实例,这就是为什么第二个示例无效的原因。
由于全局范围中定义的代码在C中是不允许的。请记住,在C中,除了变量声明/初始化之外的每行代码都必须位于函数内部。
如果您在函数内部,则可以拥有所有想要的{}
块。
因为在翻译单元的顶层只能出现声明或函数定义,不能出现复合语句(无论是否为空)。
{}
的语句是有效的。没有其他地方允许语句出现。{}
还有其他用途(命名空间、类、结构体、联合体、枚举、初始化器、链接规范),但所有这些用法都需要更多的标识符来形成有效的语法,而不仅仅是{}
。 - aschepler{ DBG("Hello"); }
在DBG
被扩展为空时将无法编译通过,这会令人惊讶。 - Matthieu M.assert
宏被禁用时,它不会产生任何结果,而是产生(void)0
或类似的结果。而你的DBG宏并不会产生一个空的复合语句{}
,而是会产生{;}
:一个包含空语句的复合语句。当然,有时候拥有空的复合语句是很有用的,而宏就是一个例子,但是考虑到assert
的定义,我不想说标准委员会期望人们编写最终扩展为空的宏,因为他们自己避免了这种情况。 - Steve Jessop{}
在文件作用域上是有效的。命名空间定义以关键字namespace和可选名称开头,因此在此之后{}
是有效的,但不能单独使用。同样,在C++中,struct foo {};
是有效的,但这也不意味着仅使用{}
是有效的。在C和C++中,void foo() {}
是有效的 - 同样,这并不允许没有引言的{}
。 - Steve Jessop;
!不这样做的原因显然是为了不搞乱一行代码:当DBG
扩展为空时,带有嵌入式;
的while(--i) DBG(x[i])
将会让每个人都感到惊讶!另一方面,将宏扩展为(void)0
,即使被优化掉,宏仍然扩展为一个正确的语句。 - Matthieu M.