web-dev-qa-db-ja.com

中括弧のない「if」ブロックは、後続の「else if」を入れ子にします

AFAIK、「if」ブロックが中括弧が提供されていない場合、1つのステートメントのみがその中と見なされます。例えば.

if(..)
  statement_1;
  statement_2;

タブに関係なく、ifブロック内ではstatement_1のみが考慮されます。

次のコードはそれとうまくいきません:

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

上記のコードは何も出力しません。 "true"が印刷されているはずです。
これは、else if以降、outerifブロック内に自動的にネストされます。 g++ -Wallは警告を発行しますが、ここでは問題ではありません。中括弧を置くと、すべてが期待どおりにうまくいきます。

なぜそのような異なる動作ですか?
[GCCデモ: 中括弧なし および 中括弧付き ]。

21
iammilind

動作は実際には異なっていませんが、完全に一貫しています。else ifを含む内部のifブロック全体がoneブロックと見なされます。

これは “ dangling -else problem” として知られる、解析における古典的な曖昧さです。通常のBNFで文法が記述されている場合、これを解析するには2つの有効な方法があります。

末尾のelseは、外側のブロックまたは内側のブロックの一部です。

ほとんどの言語は、ブロックがパーサーによって貪欲に一致することを(任意に)決定することにより、あいまいさを解決します。つまり、else [if]が最も近いifに割り当てられます。

52
Konrad Rudolph

elseは実際にはinnerifでグループ化されているため、外側ではグループ化されていません。実際には次のように解析されています

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

中かっこを必要な場所に明示的に配置することで、問題を解決できます。

6
BenW

これは、Cパーサーの観点からは非常に自然なことです。

パーサーは、if-statementを解析するときに、まず条件式を解析し、次に条件の後の最初のステートメントを解析し、次にelseキーワードを探し、elseが存在する場合は解析します2番目の(代替)ステートメント。

ただし、最初のステートメントもifステートメントであるため、パーサーは「if-parser」を再帰的に呼び出します(elseキーワードをテストする前に!)。この再帰呼び出しは、内部のif-elseステートメントを完全に解析し(elseを含む)、トークンの位置をコード全体の「終わりを超えて」移動します。

代替動作を実装する試みは、「外部」と「内部」のifパーサー間の追加の通信を伴う必要があります。外部パーサーは、「内部」に貪欲にならないように通知する必要があります(つまり、elseを食べないようにします)ステートメント)。これにより、言語構文がさらに複雑になります。

2
user396672

何も印刷されません。 2番目のif/else ifが最初のブロックに属する1つのブロックであるため、これはこれと同等です。

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

elseステートメントは常に最も近いifにアタッチされます。ネストされたifがなければ、それ自体は意味のあるステートメントを形成しないため、パーサーは続行します。

1
Roman Saveljev