C言語の選択文と繰返し文の本体は、常に複合文にするというスタイルを紹介します。
どう読める?
次のC言語のコード片を見てください。
if (condition1)
if (condition2)
func2();
else
func1();
これはインデント幅とは異なり、以下のように解釈されます。ぶら下がり else の問題と呼ばれています。
if (condition1)
if (condition2)
func2();
else
func1();
「そのelseの前に最も近い位置にあるelseのないifと結びつく」と標準で定められています。
次のコード片があります。
if (condition1)
func1();
これを次のように修正したとします。
if (condition1)
func1();
func2();
式 condition1 が0以外の場合、func1しか呼び出されません。インデントの深さと動作が一致していません。
C言語の選択文(if文、switch文)と繰返し文(while文、do-while文、for文)は、条件を表す式の後、ひとつの文しか範囲としません。このため、この文を複合文({}で囲う)とすると、紹介した問題は発生しません。
本文を複合文とすると以下となります。読み間違いにも変更にも強いコードとなっています。
次のコードは、インデント幅と選択の深さが一致しています。
if (condition1) {
if (condition2) {
func2();
}
} else {
func1();
}
次のコードも条件文の本文に行を追加しても意図通りに動きます。
if (condition1) {
func1();
}
コーディングガイドライン
MISRA C 最新版(2023)の Rule 15.6 では、以下の趣旨のガイドラインを紹介しています。
繰返し文や選択文の本体は複合文にしよう。
Python は、インデントの深さで、繰返し文や選択文の本体の範囲を示します。このため、見た目と制御構造が一致します。C言語(やそれの子孫の言語)でのトラブルから、この仕様を採用したと考えています。
わたしの場合
わたしが趣味で書いているプログラムも、このガイドライン(Rule 15.6)に従っています。
競技プログラミングでは、速く書くために、必要最低限の文しか複合文にしない傾向があります。ただし、仕事で書くプログラムのスタイルと合わせる意図もあり、MISRA C ガイドラインのスタイルを採用しています。まぁ、私の実力では、競技プログラミングでは悩んでいる時間の方が多く、そこまでタイプ量を節約する意味がない、という事情もあります。
最後に
Brian W. Kernighan と Dennis M. Ritchie によって書かれたC言語の原典である「プログラミング言語C」は、必要な場合しか複合文にしてしていませんでした。必要最低限のタイプ量で済ませたいという著者らの意図があったと思われます。いまとなっては推薦しにくいスタイルでしょうか。
C言語を使いこなしていきましょう!