如果没有花括号的“if”块会使得后续的“else if”嵌套。

24

据我所知,如果一个“if”块没有提供花括号,则只会将其中的1个语句视为在其内部。例如:

if(..)
  statement_1;
  statement_2;

不考虑制表符,只有statement_1被视为在if块内。

以下代码与此不兼容:

int main ()
{
  if(false)  // outer - if
    if(false)  // nested - if
      cout << "false false\n";
  else if(true)
    cout << "true\n";
}

以上代码没有输出任何内容。它应该输出"true"
看起来else if自动嵌套在了外部if块内。虽然g++ -Wall会给出警告,但这不是问题的关键。一旦你加上花括号,一切都按预期进行。

为什么会出现这样不同的行为?
[GCC演示:没有花括号有花括号]。


5
为什么不使用这个运算符的名称? - iammilind
2
我不明白为什么这是个问题,因为在我使用C++的时间里,我从来没有觉得这很奇怪。嵌套的“if”和它后面的所有行直到“else if”和它后面的行都是同一个“语句”的一部分,这才是重要的,行数并不重要,就像缩进一样,你自己也指出了,也不重要。 - osirisgothra
5个回答

61

这种行为实际上并没有不同,完全是一致的:整个内部if块(包括else if)被视为一个块。

这是一种经典的解析歧义,被称为“悬挂的else问题”:当语法以正常的BNF书写时,有两种有效的解析方式:

要么尾随的else是外部块的一部分,要么是内部块的一部分。

大多数语言通过(任意地)决定解析器贪婪地匹配块来解决歧义 - 也就是说,else[if]被分配给最近的if


1
链接和最后的“贪婪”行说明了一切。 - iammilind
3
这是我一段时间以来见过的最快的+10分! - Mysticial
“大多数编程语言”?你知道还有其他以不同方式实现这个功能的语言吗?(当然,除了强制使用括号或结束语法这些方法来避免悬挂else语句的歧义)。如果采用其他方式,就必须检查有多少个else语句是未匹配的(例如,在if if if else else中,第二个else将与第二个if匹配,而在if if if else中,它将与第一个if匹配)。 - AProgrammer
3
@AProgrammer,我不了解其他语言,只是想保险起见。我确信至少有一些实验性语言会因为某种原因而采用不同的方式。 - Konrad Rudolph
Python使用缩进来解决问题(那是一种“结束语法”吗?) - Karoly Horvath
在某种程度上是这样的,因为解析器将缩进(更具体地说,是特殊标记INDENT和DEDENT)视为显式块分隔符。 - Konrad Rudolph

6
因为else实际上是与内部的if组合在一起,而不是外部的if。它实际上被解释为:
int main ()
{
  if(false)  // outer - if (never gets executed)
  { 
    if(false)  // nested - if
    {
        cout << "false false\n";
    } else if(true) {
        cout << "true\n";
    }
  }
}

您可以通过在需要的位置明确放置大括号来解决问题。


2

它不应该打印任何东西。因为第二个if/else if是属于第一个if的一个块,所以这与以下代码等效:

  if(false) {
    if(false)  // nested - if
      cout << "false false\n";
    else if(true)
      cout << "true\n";
  } 

正确,现在的问题是..为什么? - iammilind
@iammilind 为什么是“为什么”? if (...) else 是一个语句。这就是全部。外部的 if 后面跟着一个语句,该语句直到 else 中的 if 结束。这与正则表达式相同 - 使用 (.*)/(.*) 匹配 a/b/c,应该将组1设置为 a,组2设置为 b/c 还是将组1设置为 a/b,组2设置为 c?后者是正确的,因为正则表达式是“贪婪”的,C解析器也是如此。 - glglgl
1
@glglgl FYI 我在 iammilind 的评论之后才插入了“since...”。起初,我只是说它们是等价的,因为我认为这是显而易见的,但正如你从票数中看到的那样,它需要更多的澄清 :) - Michael Chinen

2

从 C 解析器的角度来看,这是很自然的。

解析器在解析 if 语句时,首先解析条件表达式,然后解析条件后的第一条语句,接着查找 else 关键字,如果存在,则解析第二个(可选的)语句。

然而,第一条语句也是一个 if 语句,所以解析器在测试 else 关键字之前会递归调用“if-解析器”。这个递归调用完全解析了内部的 if-else 语句(包括 else),并将标记位置“移动到”整个代码片段的结尾。

任何试图实现替代行为的尝试都应涉及“外部”和“内部” if-解析器之间的一些额外通信:外部解析器应通知“内部”不要贪心地吃掉 else 语句。这将增加语言语法的复杂性。


1

else语句总是附加到最近的if。如果没有嵌套分支,if本身不会形成有意义的语句,因此解析器会继续执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接