Skip to content

Instantly share code, notes, and snippets.

@tenpoku1000
Last active December 17, 2022 02:17
Show Gist options
  • Save tenpoku1000/ed4676b566718341f334e7096e7d261b to your computer and use it in GitHub Desktop.
Save tenpoku1000/ed4676b566718341f334e7096e7d261b to your computer and use it in GitHub Desktop.
イマドキの C 言語の if 文では dangling(ぶら下がり・ぶらぶら・宙ぶらりん) else 問題は発生しない

イマドキの C 言語の if 文では dangling(ぶら下がり・ぶらぶら・宙ぶらりん) else 問題は発生しない

2022/12/17 更新

この記事は、自作OS Advent Calendar 2021の 12/02 の記事として書かれました。

注意:この記事のタイトルと内容は間違っています(2022/12/17 追記)

もし、「elseは,構文規則で許されるifのうち,そのelseの前で最も近い位置にあるifと結び付く。」という規定が
なかったら、C89/C90 等では dangling else 問題が生じますが、C99 以降の「選択文はブロックとする。各副文も
ブロックとする。」という規定があれば、dangling else 問題が生じないはずだという思い込みを記事として書いたものです。
自作の C コンパイラでは、バックトラックのある正規右辺文法を用いた再帰降下構文解析をしているのですが、
文法の右辺の最大の長さにマッチするようにしています。これは結果として、「elseは,構文規則で許されるifのうち,
そのelseの前で最も近い位置にあるifと結び付く。」という規定と同じ実装になっているため、この記事のタイトルと
内容は間違っていることになります。お詫びして訂正いたします。

なお、C99 以降の「選択文はブロックとする。各副文もブロックとする。」という規定については、以下の文書の
6.8 Statements and blocks をご覧ください。

C99 Rationale V5.10
https://open-std.org/JTC1/SC22/WG14/www/C99RationaleV5.10.pdf

以下のページで、同じ内容を日本語で簡略化して書かれているものを読むことが出来ます。

プログラミング言語 C の新機能
7.10 選択文と反復文のブロック化
http://seclan.dll.jp/c99d/c99d07.htm#dt19991108

記事訂正前のまえがき

初めに補足しておきますが、本稿は個人的に C 言語の規格書を読んで解釈した結果を書いたメモです。
C 言語の規格書の誤りの指摘ではありません。丹念に読み進めていくと、何を言いたいのか明らかになると思います。

dangling(ぶら下がり・ぶらぶら・宙ぶらりん) else 問題とは

C99 よりも前の C89/C90 等では、C99 の「選択文はブロックとする。各副文もブロックとする。」という規定が
無かったため、「elseは,構文規則で許されるifのうち,そのelseの前で最も近い位置にあるifと結び付く。」
という規定に基づいて「if ( 式 ) 文 else 文」の文法の曖昧性を解消していました。
具体的には、

if(a)
  if(b) f();
else g();

というコードは、以下のように解釈されます。

    if (a)
        if (b)
            f();
        else
            g();

もし、「elseは,構文規則で許されるifのうち,そのelseの前で最も近い位置にあるifと結び付く。」という規定が
無かったら、以下のようにも解釈される可能性があります。これが「dangling(ぶら下がり・ぶらぶら・宙ぶらりん) else 問題」です。

    if (a)
        if (b)
            f();
    else
        g();

イマドキの C 言語の if 文では dangling(ぶら下がり・ぶらぶら・宙ぶらりん) else 問題は発生しない

C99 以降の「選択文はブロックとする。各副文もブロックとする。」という規定から、
「if ( 式 ) 文 else 文」の文法は曖昧性が解消されるため、従来からの規定の「elseは,構文規則で許される
ifのうち,そのelseの前で最も近い位置にあるifと結び付く。」を同時に満たすと解釈しました。
具体的には、

if(a)
  if(b) f();
else g();

というコードは、以下のように解釈されるため、教科書的な「dangling(ぶら下がり・ぶらぶら・宙ぶらりん) else 問題」は
発生しないと思うのですが、そうではないという事実がありましたら、ご指摘ください。

    {
        if (a){
            if (b){
                f();
            }else{
                g();
            }
        }
    }

なお、従来からの規定の「elseは,構文規則で許されるifのうち,そのelseの前で最も近い位置にあるifと結び付く。」は
C99 以降では特に不要にも思えてきますが、「選択文はブロックとする。各副文もブロックとする。」という規定から
推測できるとは限らないですから、依然として明記しておくべき規則であることに変わりはないと思います。

参考:複合文(ブロック)と if 文の概略(意味規則)

JIS X 3010:2003 プログラム言語C | 日本規格協会 JSA Group Webdesk
https://webdesk.jsa.or.jp/books/W11M0090/index/?bunsyo_id=JIS%20X%203010:2003

6.8 文及びブロック

構文規則
    文:
        ラベル付き文
        複合文
        式文
        選択文
        繰返し文
        分岐文

意味規則 文(statement)は,実行すべき動作を規定する。別に規定する場合を除き,文は順番に実行する。
ブロック(block)は,幾つかの宣言及び文を一つの構文的な単位にまとめることを可能にする。

6.8.2 複合文

構文規則
    複合文:
        { ブロック構成要素並びopt }
    ブロック構成要素並び:
        ブロック構成要素
        ブロック構成要素並び ブロック構成要素
    ブロック構成要素:
        宣言
        文

意味規則 複合文(compound statement)はブロックとする。

6.8.4 選択文

構文規則
    選択文:
        if ( 式 ) 文
        if ( 式 ) 文 else 文
        switch ( 式 ) 文

意味規則 選択文は,制御式の値に応じて幾つかの文の中から一つを選択する。
選択文はブロックとする。そのブロックの有効範囲は,それを囲むブロックの有効範囲の真部分集合と
する。各副文もブロックとする。そのブロックの有効範囲は,選択文の有効範囲の真部分集合とする。

6.8.4.1 if文

制約 if文の制御式は,スカラ型をもたなければならない。

意味規則 両形式とも,式の値が0と比較して等しくない場合,最初の副文を実行する。else形式の場合,
式の値が0と比較して等しいならば,2番目の副文を実行する。ラベルを通して最初の副文に制御が
到達した場合,2番目の副文は実行しない。
elseは,構文規則で許されるifのうち,そのelseの前で最も近い位置にあるifと結び付く。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment