else句を使用してif…else if構文を終了すると、どのような利点がありますか?


136

私たちの組織には、次のような必須のコーディング規則(説明なし)があります。

if…else ifコンストラクトをelse句で終了する必要がある

例1:

if ( x < 0 )
{
   x = 0;
} /* else not needed */

例2:

if ( x < 0 )
{
    x = 0;
}
else if ( y < 0 )
{
    x = 3;
}
else    /* this else clause is required, even if the */
{       /* programmer expects this will never be reached */
        /* no change in value of x */
}

これはどのエッジケースを処理するように設計されていますか?

その理由について私が気になるのは、例1では必要ないelse例2では必要だということです。その理由が再利用性と拡張性であるelseならば、どちらの場合にも使用すべきだと思います。


30
会社に理由とメリットを尋ねてみてください。一見すると、プログラマはそれについて考え、「アクションは必要ありません」というコメントを追加する必要があります。Javaのチェックされた例外の背後にある(そして少なくとも議論の余地がある)同じ理由。
Thilo、2016年

32
例2は、実際にはassert(false, "should never go here")意味があるかもしれない場所の良い例です
Thilo

2
私たちの組織にも同様のルールがありますが、これほど細かくはありません。私たちの場合の目的は2つあります。まず、コードの一貫性。第二に、緩い文字列/読みやすさ。elseが必要な場合は、不要な場合でも、ドキュメントに記載されていない場合でもコードが明確になります。elseを要求することは、必要がない場合でも、アプリで可能なすべての結果を確実に考慮に入れたことを確認するために、過去に行ったことです。
LuvnJesus

4
あなたはいつでも敗北する可能性がありif (x < 0) { x = 0; } else { if (y < 0) { x = 3; }}ます。または、単にそのようにする必要があるからといって、そのようなルールの多くに従うことはできません。
ジムバルター2016年

3
@Thilo私は少し遅れていますが、まだ誰も間違いを犯していません:elseが起こらないはずの兆候はなく、副作用がないはずです(< 0チェックを行うと正常に見える)ので、アサートが行われます値が予想される範囲内にある最も一般的なケースでプログラムをクラッシュさせます。
Loduwijk

回答:


148

別の回答で述べたように、これはMISRA-Cコーディングガイドラインによるものです。目的は、防御的プログラミングであり、ミッションクリティカルなプログラミングでよく使用される概念です。

つまり、あらゆるあるif - else ifと必須の終わりelse、すべてがswitchで終わらなければなりませんdefault

これには2つの理由があります。

  • 自己文書化コード。を書いelseて空のままにすると、「私は間違いなく、どちらifも当てはまらないシナリオを検討しました」という意味になりますelse if

    else「そこに書いていない」とは、「どちらifelse if真ではないシナリオを検討したか、完全に検討するのを忘れて、コードに重大なバグがある可能性がある」という意味です。

  • 暴走コードを停止します。ミッションクリティカルなソフトウェアでは、非常にありそうもないことを説明する堅牢なプログラムを作成する必要があります。だからあなたは次のようなコードを見ることができました

    if (mybool == TRUE) 
    {
    } 
    else if (mybool == FALSE) 
    {
    }
    else
    {
      // handle error
    }

    このコードは、PCプログラマーやコンピューターサイエンティストにはまったく関係がありませんが、「mybool」が何らかの理由で破損した場合を検出するため、ミッションクリティカルなソフトウェアでは完全に理にかなっています。

    歴史的には、EMI /ノイズのためにRAMメモリの破損を恐れていました。これは今日の問題の多くではありません。多くの場合、メモリの破損は、コードの他の場所にあるバグが原因で発生します:間違った場所へのポインター、配列外のバグ、スタックオーバーフロー、暴走コードなど。

    そのため、ほとんどの場合、このようなコードは、実装段階でバグを記述したときに戻ってきます。これは、デバッグ手法としても使用できることを意味します。作成しているプログラムは、バグを作成したことを通知します。


編集

なぜelse毎回必要でないのかについてif

if-elseまたはif-else if-else完全に変数を持つことができるすべての可能な値をカバーしています。しかし、ifすべての可能な値をカバーするための単純なステートメントが必ずしも存在するわけではなく、はるかに広い用途があります。ほとんどの場合、特定の条件を確認するだけで、条件が満たされない場合は何もしません。次に、elseケースをカバーするために防御的プログラミングを書くことは単に意味がありません。

さらにelse、毎回空を書き込んだ場合、コードが完全に乱雑になりますif

MISRA-C:2012 15.7には理由elseが説明されておらず、なぜ必要ないのかが示されています。

注:else単純なif ステートメントの場合、最後のステートメントは必要ありません。


6
メモリが破損している場合、チェックコード自体を含めて、myboolだけでなく、それ以上の問題が発生すると予想しています。if/else if/elseコンパイルしたことを期待どおりに検証する別のブロックを追加してみませんか?そして、以前のベリファイアを確認するためにもう1つ?
Oleg

49
はぁ。皆さん、デスクトッププログラミング以外にまったく経験がないのであれば、明らかに経験のないことについてすべてを知っているコメントをする必要はありません。これは、防御的プログラミングが議論され、PCプログラマーが立ち寄ったときに常に発生します。理由のために、「このコードはPCプログラマーにとって完全に異質なものになる」というコメントを追加しました。RAMベースのデスクトップコンピューターで実行するように安全性が重要なソフトウェアをプログラムすることはありません。限目。コード自体は、ECCおよび/またはCRCチェックサムを備えたフラッシュROMに常駐します。
ランディン2016年

6
@Deduplicator確かに(myboolCがそれ自身を取得する前のケースのように非ブール型でない限りbool、コンパイラーは追加の静的分析なしに仮定を行いません)。そして、「elseを記述しても空のままにしておくと、「ifもelse ifも真でない場合のシナリオを確実に検討しました」という件名になっています。 elseブロック、それ以外の場合、なぜそこに座っているだけの空のelseブロックがあるのですか?// unusedコメントだけではなく、空のブロック、適切であろう。
JAB 2016年

5
@JABはいelse。コードがない場合、ブロックには何らかのコメントを含める必要があります。空の一般的な方法elseは、単一のセミコロンとコメント:else { ; // doesn't matter }です。それ以外の場合、インデントされた単一のセミコロンを自分の行に入力する理由はありません。同様の方法は、空のループでも使用されることがありますwhile(something) { ; // do nothing }。(明らかに改行付きのコードです。SOコメントでは許可されていません)
Lundin

5
2つのIFを含むこのサンプルコードは、マルチスレッド環境の通常のデスクトップソフトウェアでも他の場所に移動できるため、myboolの値をちょうどその間に変更できることに
注意してください

61

あなたの会社はMISRAコーディングガイダンスに従いました。このルールを含むこれらのガイドラインのいくつかのバージョンがありますが、MISRA-C:2004から

ルール14.10(必須):すべてのif…else if構文は、else句で終了する必要があります。

このルールは、ifステートメントの後に1つ以上のelse ifステートメントが続く場合に常に適用されます。最後のelseのif後にelse ステートメントが続きます。単純なifステートメントの場合は、ステートメントをelse 含める必要はありません。最終else ステートメントの要件は、防御的プログラミングです。else声明は、いずれかの適切な行動を取るか、何の処置がとられていない理由として、適切なコメントを含まなければなりません。これはdefaultswitchステートメントに最後の句があるという要件と一致しています。たとえば、次のコードは単純なifステートメントです。

if ( x < 0 )
{
 log_error(3);
 x = 0;
} /* else not needed */

一方、次のコードはifelse if構成を示しています

if ( x < 0 )
{
 log_error(3);
 x = 0;
}
else if ( y < 0 )
{
 x = 3;
}
else /* this else clause is required, even if the */
{ /* programmer expects this will never be reached */
 /* no change in value of x */
}

MISRA-C:2012、2004年版に優先して、新しいプロジェクトのための現在の推奨で、同じルールが存在するが、15.7に番号付けされます

例1: 単一のifステートメントで、プログラマーはn個の条件をチェックして単一の操作を実行する必要がある場合があります。

if(condition_1 || condition_2 || ... condition_n)
{
   //operation_1
}

通常の使用では、を使用する場合、常に操作を実行する必要はありませんif

例2: ここで、プログラマーはn個の条件をチェックし、複数の操作を実行します。通常の使用でif..else ifswitch、デフォルトのような操作を実行する必要がある場合があります。したがって、elsemisra標準に従って使用法が必要です

if(condition_1 || condition_2 || ... condition_n)
{
   //operation_1
}
else if(condition_1 || condition_2 || ... condition_n)
{
  //operation_2
}
....
else
{
   //default cause
}

これらの資料の現在および過去のバージョンは、MISRAウェブストアを介してから購入できます。


1
ありがとう、あなたの答えはすべてミスラのルールの内容ですが、私は質問の編集部分での混乱に対する答えを期待しています。
Trevor

2
いい答えだ。好奇心から、これらのガイドは、else条項に到達できないと予想される場合に何をすべきかについて何か言いますか?(代わりに最終条件を
除外して

17
著作権を侵害する盗用とリンクには反対票を投じます。投稿を編集して、あなたの言葉とMISRAの言葉がより明確になるようにします。当初、この答えは生のコピー/貼り付けにすぎませんでした。
ランディン2016年

8
@TrieuTheVan:質問は移動目標であってはなりません。投稿する前に、質問が完全であることを確認してください。
TJクラウダー2016年

1
@ jpmc26いいえ。ただし、MISRAの目的は、厳密なコードを提供することではなく、安全なコードを提供することです。最近のコンパイラーは到達不能なコードをとにかく最適化するので、欠点はありません。
Graham

19

これは、すべてのスイッチでデフォルトのケースを必要とするのと同じです。

この余分なものは、プログラムのコードカバレッジを減らします。


LinuxカーネルまたはAndroidコードを別のプラットフォームに移植した経験から、私たちは何度も何か問題を起こし、logcatには次のようなエラーが表示されます

if ( x < 0 )
{
    x = 0;
}
else if ( y < 0 )
{
    x = 3;
}
else    /* this else clause is required, even if the */
{       /* programmer expects this will never be reached */
        /* no change in value of x */
        printk(" \n [function or module name]: this should never happen \n");

        /* It is always good to mention function/module name with the 
           logs. If you end up with "this should never happen" message
           and the same message is used in many places in the software
           it will be hard to track/debug.
        */
}

2
これは、メッセージが出力された場合にソースの場所を見つけやすくするために__FILE____LINE__マクロが役立つ場所です。
Peter Cordes

9

私がこれをすべて約5年前に行ったので、簡単な説明のみ。

(ほとんどの言語で)「null」elseステートメントを含めるための構文要件はありません(そして不要な{..})はなく、「単純な小さなプログラム」では必要ありません。しかし、実際のプログラマーは「単純な小さなプログラム」を作成せず、同じくらい重要なこととして、一度使用されて破棄されるプログラムを作成しません。

if / elseを書くとき:

if(something)
  doSomething;
else
  doSomethingElse;

それはすべて単純に見え、追加する意味すらほとんどわかりません{..}

しかし、いつの日か、数か月後、他のプログラマー(このような間違いを犯すことは決してないでしょう!)は、プログラムを "強化"し、ステートメントを追加する必要があります。

if(something)
  doSomething;
else
  doSomethingIForgot;
  doSomethingElse;

突然doSomethingElseelse脚にあるはずのことを忘れてしまいます。

したがって、あなたは良い小さなプログラマーであり、常にを使用します{..}。しかし、あなたは書きます:

if(something) {
  if(anotherThing) {
    doSomething;
  }
}

その新しい子供が真夜中の変更を行うまで、すべてが順調です。

if(something) {
  if(!notMyThing) {
  if(anotherThing) {
    doSomething;
  }
  else {
    dontDoAnything;  // Because it's not my thing.
  }}
}

はい、それは不適切にフォーマットされていますが、プロジェクト内のコードの半分も同様であり、「オートフォーマッター」はすべての#ifdefステートメントによって混乱しています。そしてもちろん、実際のコードはこのおもちゃの例よりもはるかに複雑です。

残念ながら(またはそうではありませんが)、私はここ数年このような状況から抜け出しているので、新鮮な「本当の」例を念頭に置いていません。


7

これは、後で参照できるようにコードを読みやすくするため、および最後のレビュー担当者が最後elseので処理された残りのケースは何しないことを明確にするために行われますも一見して見落とされないようにします。

これは優れたプログラミング手法であり、コードを再利用および拡張可能にします。


6

私は以前の答えに追加したいと思います-そして部分的に矛盾します-。式の考えられる値の全範囲をカバーする必要があるスイッチのような方法でif-else ifを使用することは確かに一般的ですが、可能な範囲の条件が完全にカバーされることは決して保証されていません。同じことがスイッチコンストラクト自体についても言えるため、残りのすべての値をキャッチするdefault句を使用する必要があり、特に必要がない場合でも、アサーションセーフガードとして使用できます。

質問自体は良い反例を備えています:2番目の条件はxにまったく関連していません(これが、スイッチベースのバリアントよりも柔軟なifベースのバリアントをしばしば好む理由です)。この例から、条件Aが満たされる場合、xを特定の値に設定する必要があることは明らかです。Aが満たされない場合、条件Bがテストされます。それが満たされている場合、xは別の値を受け取る必要があります。AもBも満たされない場合、xは変更されません。

ここで、読者に対するプログラマの意図についてコメントするには、空のelseブランチを使用する必要があることがわかります。

一方、特に最新かつ最も内側のifステートメントにelse句が必要な理由はわかりません。Cでは、「else if」のようなものはありません。ifとelseだけがあります。代わりに、MISRAによると、構成はこのように正式にインデントする必要があります(また、開始中括弧を独自の行に配置する必要がありましたが、それは好きではありません)。

if (A) {
    // do something
}
else {
    if (B) {
        // do something else (no pun intended)
    }
    else {
        // don't do anything here
    }
}

MISRAがすべてのブランチを中括弧で囲むことを要求するとき、それは「if ... else if構造」に言及することによってそれ自体と矛盾します。

誰もが深くネストされたif elseツリーの醜さを想像できます。サイドノートのこちらを参照してください。この構成が任意の場所に任意に拡張できると想像してください。その後、最後にelse句を要求することは、他の場所では要求しないことはばかげたことになります。

if (A) {
    if (B) {
        // do something
    }
    // you could to something here
}
else {
    // or here
    if (B) { // or C?
        // do something else (no pun intended)
    }
    else {
        // don't do anything here, if you don't want to
    }
    // what if I wanted to do something here? I need brackets for that.
}

ですから、MISRAガイドラインを作成した人々は、意図的にスイッチのようなif-elseを考えていたと思います。

最後に、「if ... else ifコンストラクト」の意味を正確に定義することが重要になります


5

基本的な理由は、おそらくコードカバレッジと暗黙のelseです。条件がtrueでない場合、コードはどのように動作しますか?真のテストでは、条件falseでテストしたことを確認する方法が必要です。すべてのテストケースがif句を通過する場合、テストしなかった条件が原因で、コードに実際の問題が発生する可能性があります。

ただし、一部の条件は、納税申告書などの例1のように正しくなる場合があります。「結果が0未満の場合は、0を入力します。」それでも、条件が偽であるテストが必要です。


5

論理的には、どのテストも2つの分岐を意味します。それが真の場合はどうしますか、それが偽の場合はどうしますか。

どちらのブランチにも機能がない場合は、機能が必要ない理由についてコメントを追加するのが妥当です。

これは、次のメンテナンスプログラマが一緒にやって来る場合に役立ちます。コードが正しいかどうかを判断するために、遠くまで検索する必要はありません。捕まえてください

個人的には、elseのケースを見て評価することを強いられるので、私を助けてくれます。それは不可能な条件である可能性があり、その場合、契約に違反しているために例外をスローする可能性があります。害のない場合は、コメントで十分です。

あなたのマイレージは異なる場合があります。


4

ほとんどの場合、ifステートメントが1つしかない場合、それはおそらく次のような理由の1つです。

  • 機能ガードチェック
  • 初期化オプション
  • オプションの処理ブランチ

void print (char * text)
{
    if (text == null) return; // guard check

    printf(text);
}

しかし、あなたがそうするときif .. else if、それはおそらく次のような理由の1つです:

  • ダイナミックスイッチケース
  • フォークの処理
  • 処理パラメーターの処理

そして、あなたif .. else ifがすべての可能性をカバーする場合、その場合、あなたの最後if (...)は必要ありません、あなたはそれを単に取り除くことができます。

int absolute_value (int n)
{
    if (n == 0)
    {
        return 0;
    }
    else if (n > 0)
    {
        return n;
    }
    else /* if (n < 0) */ // redundant check
    {
        return (n * (-1));
    }
}

そして、これらの理由のほとんどで、何かがのどのカテゴリーにも適合しない可能if .. else if性があるため、最終else節でそれらを処理する必要があります。処理は、ビジネスレベルの手順、ユーザー通知、内部エラーメカニズムを通じて実行できます。 ..等。

#DEFINE SQRT_TWO   1.41421356237309504880
#DEFINE SQRT_THREE 1.73205080756887729352
#DEFINE SQRT_FIVE  2.23606797749978969641

double square_root (int n)
{
         if (n  > 5)   return sqrt((double)n);
    else if (n == 5)   return SQRT_FIVE;
    else if (n == 4)   return 2.0;
    else if (n == 3)   return SQRT_THREE;
    else if (n == 2)   return SQRT_TWO;
    else if (n == 1)   return 1.0;
    else if (n == 0)   return 0.0;
    else               return sqrt(-1); // error handling
}

この最後のelse句は、Javaand などの言語の他のいくつかのものと非常に似てC++います。

  • default switchステートメントのケース
  • catch(...)それはすべての特定のcatchブロックの後に来ます
  • finally try-catch句内

2

私たちのソフトウェアはミッションクリティカルではありませんでしたが、防御的なプログラミングのため、このルールを使用することも決定しました。理論的に到達できないコード(switch + if-else)にスロー例外を追加しました。また、新しいタイプが追加された場合や、1つまたは2つのif-elseまたはスイッチを変更するのを忘れた場合など、ソフトウェアが高速で失敗したため、何度も節約できました。ボーナスとして、それは問題を見つけるのを非常に簡単にしました。


2

ええと、私の例には未定義の動作が含まれていますが、一部の人々は空想になり、一生懸命失敗することがあります。

int a = 0;
bool b = true;
uint8_t* bPtr = (uint8_t*)&b;
*bPtr = 0xCC;
if(b == true)
{
    a += 3;
}
else if(b == false)
{
    a += 5;
}
else
{
    exit(3);
}

おそらく持っていることを期待することはないboolされていないtruefalse、しかし、それが起こることがあります。個人的には、これは空想的なことをすることを決めた人によって引き起こされる問題だと思いますが、追加のelseステートメントはそれ以上の問題を防ぐことができます。


1

現在PHPを使用しています。登録フォームとログインフォームを作成します。ifとelseだけを使用しています。他に必要なものはありません。

ユーザーが送信ボタンをクリックした場合->次のifステートメントに移動します...ユーザー名が「X」文字未満の場合は警告します。成功した場合は、パスワードの長さなどを確認してください。

サーバーの読み込み時間の信頼性を無視してすべての追加コードをチェックできる場合は、elseなどの追加コードは必要ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.