ソフトウェア技術者のためのバグ百科事典(17)無限ループやbreak抜け、コーディングのバグ再び:山浦恒央の“くみこみ”な話(138)(1/3 ページ)
ソフトウェア技術者に向けて、バグに関する基礎知識をまとめていく新シリーズ「バグ百科事典」。第17回は、第14回でテーマにしたコーディングのバグを再び取り上げます。無限ループやbreak抜けなどまだまだあります!
1.はじめに
バグ百科事典では、筆者が気になったバグを紹介し、読者の皆さまに「バグの早期発見」と「バグの未然防止」に役立てていただくものです。
少しずつ、バグの知識が深まってきたでしょうか。今回は、誰もが経験するコーディングのバグを取り上げます。
2.コーディングフェーズのバグ
ソフトウェア開発業務で最も楽しい作業が、コーディングフェーズでしょう。一般の人々は、プログラム開発=コーディングとの認識があるようですが、ソフトウェア開発の一つのフェーズにすぎません。このフェーズでは、仕様や設計を基にしてプログラムを記述します。
楽しい作業の中にも苦しみは存在します。特に、コンピュータは、人間が指示した通りにしか動作しませんので、自分の思い通りのプログラムを書くことは簡単ではありません。例えば、ループ処理を考えてみましょう。人間であれば、何回も同じ作業を続けていると、「もうやりたくない」「もう十分作業したよね」と思いますが、コンピュータは違います。コンピュータは、とにかく言われた通りに、電源がOFFになるまで作業を続けるのです。
コーディングフェーズのバグは、連載第135回「ソフトウェア技術者のためのバグ百科事典(14)地獄の作業と化すコーディングのバグ」で取り上げ、その際、下記のバグを紹介しました。
- 変数の未初期化
- 似た名前の変数を間違える
- 関数の使用方法を間違える
- 意図しないキャストが発生する
上記は、常連のコーディングのバグです。一見、単純なバグと思いますが、プログラムを結合して、システム全体の挙動を見ると、非常に不可解な動作をします。よって、この単純バグにも目を向けることは大切です。
今回は、上記以外のコーディングのバグについて、「よくあるコーディングのバグ(その2)」として紹介します。なお、説明はC言語をベースにしています。
3.よくあるコーディングのバグ(その2)
よくあるコーディングのバグを以下に示します。
3.1 無限ループ
プログラムを書くメリットは、同じ処理を繰り返し実行できることです。これをプログラミングでは、ループ処理と言いますね。ループ処理の典型的なバグの一つが、「無限ループ」です。例えば、「ループ内でbreakを書き忘れる」「条件分岐の関係上、ループから抜け出せない」などがあります(リスト1)。
//無限ループ while (1){ //ここから抜けられない //処理 }
リスト1は、無限ループの例です。無限ループで注意することは、ループを抜ける処理の記述を忘れないことです。この処理が抜けていると、強制終了しない限り、プログラムは終了しません。
3.2 switch文のbreak抜け
プログラムの流れを制御するには、条件分岐が最適です。条件分岐を行う構文の一つが、switch文です。switch文では、switchとcaseをセットで書き、条件に対応した処理を記述します。多くの条件を順々に書けるのがメリットですが、ループを抜けるbreak文を書き忘れると、意図した動作をしません(リスト2)。
#include <stdio.h> int main(){ int a = 1; switch (a) { case 1: printf("case 1:を実行する"); //break文が抜けている case 2: printf("case 2:を実行する"); break; } return 0; }
リスト2は、switch文の例です。switch文では、入力した値に合致する条件文を実行します。この場合、a = 1の場合は、「case 1:」、a = 2の場合は、「case 2:」を実行しますね※1)。
今回の例では、「case 1:」の処理にbreak文が入っていません。break文とはループから抜ける処理ですから、この例題では「case 1:」「case 2:」のどちらも実行することになります。
※1)defaultは、説明の便宜上、省略しています。ちなみに、defaultの書き忘れも単純バグの一つでしょう。
3.3 代入の記述ミス
プログラムを記述するのは人間ですから、記述ミスが起きます。誰しも経験する代表例の一つが、「if(a == 1)」を「if(a = 1)」と書くことですが、他にもリスト3のような例があります。
#include <stdio.h> int main() { int a = 0; a == 1; if (a == 1) { printf("この行を実行しない\n"); } return 0; }
これは、変数aに1を代入し、if以下を実行するプログラムです。一見、よさそうに見えますが、「a = 1;」と書くべきところに、「a == 1;」と書いてしまっています。この場合、変数aは0のままで、if文以下を実行できません。
3.4 領域外を書き換える
配列を扱うプログラムでやりがちなのは、宣言した配列の領域外にアクセスしたり、その中身を書き換えたりすることです(リスト4)。
#include <stdio.h> int main(){ int i; int data[5]; for (i = 0; i <= 5; ++i) { data[i] = i; } return 0; }
リスト4では、配列data[5]と宣言して、ループ内で、変数iの値を代入しています。配列は、0から数えますので、使えるのはdata[4]までです。例題では、ループ変数iを0〜5まで繰り返しており、宣言していない変数data[5]に値を代入していますね。結果として、領域外を書き換えてしまいます。
この例では不可解な事象は起きませんが、領域外を書き換えると、プログラムの停止や、意図しない動作を引き起こします。注意してください。
Copyright © ITmedia, Inc. All Rights Reserved.