ソフトウェア技術者に向けて、バグに関する基礎知識をまとめていく新シリーズ「バグ百科事典」。第13回は、「デバッガの使用方法」と「ビルド構成に関するバグ」を取り上げます。
バグ百科事典では、筆者が気になったバグを紹介し、読者の皆さまに「バグの早期発見」と「バグの未然防止」に役立てていただくものです。
今回は、「デバッガの使用方法」と「ビルド構成に関するバグ」を取り上げます。特に、プログラムが苦手な学生、チームに入りたての新人社員やOJT担当のエンジニアの方に見ていただき、日頃の開発業務のヒントにしていただければと思います。
そろそろ秋となり、研修を終えた新人エンジニアがプログラムを書き始めるころでしょう。「即戦力のある新人」を期待したいところですが、筆者の経験上、新人がすぐに戦力になることはほとんどありません。大抵は、先輩の指示を聞きながらコードの断片を作るぐらいで、最終的には、新人の書いたコードが跡形もなくなることも少なくありません。筆者が新人の頃もそうでした。
新人は、「より深いプログラミングのテクニック」「異常ケースの考察」「ソフトウェア業界の慣例」など、学ぶことはたくさんあります。さまざまな情報を前提とするため、思い通りのプログラムが作れずに苦労します。
その際に最も役に立つツールが「デバッガ」です。デバッガを使うと、コードの流れを1行ずつ追跡できるため便利ですが、新人によってはデバッガの使い方を知らない場合も少なくありません。例えば、ゼロ除算が発生し、プログラムがどこかで強制終了するプログラムがあるとします。デバッガがあれば、怪しい場所からプログラムを一時停止し、一行ずつ実行できますが、デバッガがないと、あらゆる変数を出力しながら目視で動作を追うことになります。心が折れますね。
今回は、そんな苦労している新人向けに、デバッグのやり方とデバッガをおさらいし、その際に見落としがちなバグを紹介します。
どんなエンジニアでも、プログラムが期待した動作をしない経験をします。その際、デバッグでバグの原因を探します。デバッグのやり方には、例えば、以下の4つがあります。
最も初歩的なやり方の一つが、標準出力によるデバッグです(リスト1)。
if (a > b){ //デバッグ出力 printf("DEBUG: %d\n",data1); }
リスト1は、標準出力の使用例です。バグの原因となりそうな場所にprintfを仕掛けて、実行結果を確認しています。出力を確認すると、「意図した値となっていない」「メモリ破壊をしている」「意図したパスに入らない」などが分かりますね。
ログによるデバッグも効果的です(リスト2)。
if (a > b){ //ログ出力 fprintf(fp, "%d", data1); }
リスト2は、ログの使用例です。バグに関係しそうな場所にログを仕込み、出力を確かめます。ログの利点は、長時間、出力結果を記録できることです。バグの原因が分からない場合、疑わしい場所にログを入れて、長い間、経過を観察できます※1)。
※1)ログに関連するバグは、連載第131回「ソフトウェア技術者のためのバグ百科事典(10)デバッグの強力な手掛かり「ログ」のバグ」で紹介しました。興味がある方はご参照ください。
デバッガとは、プログラムを特定の行で一時停止し、1行ずつ実行や、変数の中身を確認できるツールです(リスト3)。
リスト3は、デバッガの動作イメージです。「ブレークポイント」として、プログラムを一時停止する場所を設定すると、実行時にその場所でプログラムが一時停止し、一行ずつ実行したり、変数の値を確認したりすることができます。
バグを見つけるには、対象範囲を狭めることも有効です。その方法の一つが、関連がある部分をコメントアウトし、その部分を「なかったこと」にする方法です(リスト4)。
char *mystrcat(char *s1, const char *s2) { char* ptr; ptr = s1; while (*s1 != '\0') { s1++; } /* 以下をコメントアウト while (*s2 != '\0') { *s1 = *s2; s2++; } */ *s1 = '\0'; return ptr; }
リスト4は、コメントアウトの使用例です。バグの影響範囲を狭めるため、2つあるwhile文のうち1つをコメントアウトし、無効化しています。結果を確認することで、どこまで意図したプログラムか分かりますね。
Copyright © ITmedia, Inc. All Rights Reserved.