C 和 C++ 之间的“if”语句语法差异

Tom*_*son 93 c c++ cross-language language-lawyer

if (1) int a = 2;
Run Code Online (Sandbox Code Playgroud)

这行代码是有效的 C++ 代码(至少可以编译)但无效的 C 代码(无法编译)。我知道语言之间存在差异,但这一差异是出乎意料的。

我一直以为语法是

if (expr) statement
Run Code Online (Sandbox Code Playgroud)

但这将使其在两者中都有效。

我的问题是:

  1. 为什么这个不能用 C 编译?
  2. 为什么会存在这种差异呢?

Chr*_*odd 90

这是 C 和 C++ 之间微妙而重要的区别。在 C++ 中,任何语句都可以是声明语句。在 C 语言中,没有声明语句这样的东西;相反,在任何复合语句中都可以出现声明而不是语句

来自 C 语法(C17 规范):

复合语句:“{”块项目列表选择“}”
块项目列表块项目| 块项目列表 块项目
块项目:声明| 陈述

来自 C++ 语法(C++14 规范):

复合语句:“{”语句序列选择“}”
语句序列语句| 语句-seq 语句
语句: ... | 声明-声明| ...

目前尚不清楚为什么存在这种差异,这只是语言进化的方式。C++ 语法可以追溯到(至少)C++85。C 语法是在 C89 和 C99 之间的某个时候引入的(在 C89 中,声明必须位于块的开头)


在 C++ 的原始 85 和 89 版本中,声明语句中定义的变量的范围是“直到封闭的末尾”。因此,这样的声明if不会立即超出范围(就像在较新的版本中那样),而是位于同一块作用域中 if 后面的语句的范围内。当条件为假时,这可能会导致访问未初始化数据时出现问题。更糟糕的是,如果 var 有一个重要的析构函数,那么当作用域结束时就会调用它,即使它从未被初始化!我怀疑试图避免此类问题导致 C 采用不同的语法。

  • 恕我直言,C 转向 C++ 定义除了消除意外差异之外,还可以通过消除陷阱来简化语言。遗憾的是,他们在 C99 中没有这样做,因为遵循惯例并允许声明和定义混合。 (9认同)

bol*_*lov 75

克里斯的回答(和其他人)显示了语法的不同之处。

我想指出这if (1) int a = 2;在 C 中没有意义,但在 C++ 中却有意义。由于我们没有块,只有 1 个语句/声明,因此无法进一步使用声明的变量(它立即超出范围)。在 C 中,允许这样做是没有意义的,但在 C++ 中,构造函数和析构函数可能会产生副作用,因此定义和初始化立即超出范围的变量可能很有用,并且必须允许。

  • 但是,可以使用该地址。`int a=foo(&a);`。 (4认同)
  • @Deduplicator 好点。仅仅因为您*可以*使用该变量只是为了其构造函数/析构函数的副作用,并不意味着您*应该*。隐藏语义会导致编码变得非常晦涩。 (4认同)
  • @tstanisl 但是, `foo(&(int){0})` 语法在传递其地址之前完全初始化对象,而 `int a = foo(&a);` 语法不会触及 `a` 的内存,直到在 `foo()` 返回之后。另一个细微的差别可能会改变程序的语义。 (3认同)
  • @cmaster-reinstatemonica:如果您确实想在 C 中执行此操作,那么将“{}”包裹在包含函数调用的声明周围是很简单的。那么即使在 C89 中它也变得合法。不过,这是需要的语法的一个奇怪的怪癖,但它并不真正需要修复,因为在(我希望所有)现实世界的编译器中总是有一个等效的解决方法,即使是出于优化目的。 (2认同)

Kei*_*son 28

这是因为C和C++定义语句不同。

在 C 语言中,声明不属于语句。C 复合语句由开头、块项{的选项列表和结尾组成,其中块项可以是声明语句。(这一点在 C99 中发生了变化,C 添加了在块内混合声明和语句的功能。)}

在 C++ 中,声明被归类为语句(但前提是它位于复合语句内)。这允许更简单地定义复合语句:它是 a {,后跟可选的语句列表,最后是 a }

这种差异并没有太大的实际作用;总是可以解决它的。一个影响是,在 C++ 中紧跟在 case 标签后面的声明是合法的,但在 C 中这是不合法的。


dbu*_*ush 15

在 C 语言中,声明语句是不同的实体。

在 C++ 中,称为块声明的声明子集是一种语句类型,具体来说它是声明语句。其中包括简单的声明,例如int a=2.