C言語 デバッグの基礎【プログラムが動かないときの解決手順!】

C言語
この記事は約14分で読めます。

こんにちは、ナナです。

この記事は、プログラミング初心者の方へ向けた大事なことを解説します。特に独学でプログラミングを頑張ろうとしている方は 必見です。

皆さんはプログラミングを勉強する中で、こんなシーンを経験していませんか?

私は日ごろからプログラミング初心者の方に対して指導することが日常です。

そこでよく見る姿がこんな風に「プログラムが思うように動かず、画面を見ながら悩み続ける」そんな人たちです。このような方は本当に多くて「自分もだ…」と思った方もいることでしょう。

しかしですよ、

ボーっと画面を見てても問題は解決しませんっ!

では、どうしたら解決するのか?

それは『デバッグ』をすることなんです。

『デバッグ』という技術は、プログラミング言語の入門書籍や入門サイトでは、ほとんど紹介されることがない技術です。その技術を公開しちゃいます!

プログラミング初心者が「デバッグ」について学べる機会は本当に少ないですが、『デバッグ』技術とは初心者の人ほど活用すべき機能なんです。

この技術を使いこなせば「プログラムが動かないよ~」と悩んでいる状態から抜け出すことができるんですよ!

本記事では次の疑問点を解消する内容となっています。

本記事で学習できること
  • 「バグ」ってそもそも何なの?
  • デバッグ作業の流れとは?
  • 統合開発環境の「デバッガ機能」って何?
  • 「ブレークポイント」の機能と使い方とは?
  • 「ステップ実行」でプログラムを動かすとは?
  • 実践的なサンプルプログラムでデバッグ作業の工程を順に紹介

では、『デバッグ』技術をレベル別に解説していきましょう。

スポンサー

「バグ」と「デバッグ」の違いとは?

デバッグとは「バグ」を取り除く行為のことです。もう少し詳しく解説していきましょう!

「バグ」とはいったい何なのか?

「バグ(bug)」とは「虫」のことです。プログラムの中に問題があることを「バグがある」と表現します。

プログラムを作る過程とは「バグ」との戦いです。「プログラムが思い通りに動かない」ということは、プログラミングを始めたばかりの人ほど多く経験します。

この「バグ」を取り除くことができれば問題は無事に解決するのです。これを「デバッグ」と呼びます。

C言語におけるバグの種類

バグはC言語での開発工程において、大きく分けて2つの種類のバグがあります。

Noバグの種類内容
プログラム文法上のバグセミコロン漏れ、括弧の閉じ忘れなど。
ビルド時に文法上で発生するエラーや警告のバグ。
プログラム実行時のバグ文法は問題ないが、実行した結果が思っていた結果と異なる。
明確な間違いを指摘されないため、自分で調べる必要がある。

本記事で解説するバグの悩み解決は②の方になります。

プログラムを動かしたときに、うまく結果が出力されない。そんな時に役立つ技術が「デバッグ」技術なのです。

「デバッグ」作業の流れを解説!

簡単に「デバッグすればいい」とは言いますが、実はそれほど簡単なことではありません。プログラムの「バグ」とは実際に虫の形をしているわけでもなく、巧妙に隠れているからです。

そのため、この「バグ」を見つけるためには、正しい手順で「デバッグ」を行う必要があります。

デバッグの作業とは次のものです。

この作業の中で、初心者が最も苦戦しやすいのが「STEP2」の原因を調べる作業です。これはプログラムのどこに「バグ」が潜んでいるかを見つける作業のことです。

ナナ

「ボ~」っと見てても問題はいっこうに解決しません!デバッグスキルを身に付けて「バグ」を退治しましょう。

統合開発環境に搭載されている『デバッガ機能』を紹介します

このデバッグ作業で役に立つのが、統合開発環境の「デバッガ機能」です。本サイトで使用する統合開発環境「Visual Studio」は、優秀な「デバッガ機能」を備えています。

プログラミング初心者が、

「統合開発環境」を使う最大のメリットとは「デバッガ機能」である

といっても過言ではありません。

「デバッガ」とは、皆さんにとって「虫取り網」や「虫取りかご」のような、効率よく虫を捕まえるための道具なんです。

ナナ

「Visual Studio」以外の統合開発環境でも、同様の機能を持っています。そのため、一度覚えてしまえば、他の開発でも役に立ちますよ!

スポンサー

デバッグ技術 Level1:『ブレークポイント』と『ステップ実行』機能

「デバッガ機能」はいろいろとあり、少しずつでもよいので使いこなしていきましょう。まず、最初に身に付けるべき「レベル1」のデバッグ技術を学ぶのは、次の人を対象としています。

デバッグ技術「レベル1」を学ぶ条件
  • 「変数」と基本的な演算方法を理解している
  • 「if文」「switch文」による分岐処理を理解している
  • 「for文」「while文」による反復処理を理解している

それでは、条件をクリアしている方はデバッガ機能の基本を学んでいきましょう。

『ブレークポイント』の設定方法

『ブレークポイント』とはプログラムの実行を一時停止するための機能のことです。

デバッグをするときは、この『ブレークポイント』を怪しい部分に設定しておき、プログラムを順に見ていくことになります。

まずは、簡単な次のプログラムを例に『ブレークポイント』の設定方法を覚えましょう。

Visual Studioにプログラムをコピーして、「ビルド」を行ってください。

#include <stdio.h>

int main(void)
{
	int calc = 10;

	calc = calc + 20;

	printf("計算結果:%d", calc);

	return 0;
}

Visual Studioのエディタに『ブレークポイント』を設定しましょう。

プログラムの左に部分をクリックすると、印が表示され『ブレークポイント』が設定されたことを意味します。

『デバッグ実行』をしてプログラムを動かしてみよう!

メニューから[デバッグ]-[デバッグの開始]を行い実行しましょう。「F5」キーを押しても実行できます。

プログラムを動かすと、『ブレークポイント』の位置まで処理が進みます。

このように『ブレークポイント』とは、プログラムが順に実行されて設定されている場所に差し掛かると自動的に「一時停止の状態」とするための機能です。

黄色の矢印が止まっている場所であり、「次に実行しようとしている処理」を示しています。

ナナ

つまり、黄色の矢印の処理はまだ実行されていない状態だということに注意してください!

『ステップ実行』をしてプログラムを1行ずつ動かしてみよう!

プログラムが一時停止している状態になった後は、『ステップ実行』を行うことでプログラムを1行ずつ順に動かすことができます。

プログラムは通常ものすごく高速で動きますが、『ステップ実行』を使うことでじっくり考察しながら動かすことができるのです。

『ステップ実行』にはいくつか種類があります。レベル1では最も基本となる『ステップオーバー』を使ってみましょう。

『ステップオーバー』を1度押すことで、黄色矢印が1行進んだのがわかります。

「ステップ実行」のメニューが表示されていない人は、次の手順で表示できます。

変数の『ローカル』機能を見てみよう!

プログラムとは「情報」を処理で加工しながら動きます。バグの原因の多くは、この「情報」を正しく制御できないことにあるのです。

この情報とはプログラムで言うところの『変数』であり、『変数』の値が狙い通りの値になっていない問題が多いのです。

Visual Studioの「デバッガ」には変数の値を監視するための『ローカル』機能が搭載されています。これは、変数の情報を可視化したものです。

「ローカル」画面には、ステップ実行を行うたびに最新の変数の状況が表示されます。

よく見ると、数字が赤くなっていますね。これは、ステップ実行をしたときに変化があったことを示しています。変数をたくさん利用している時は、この赤くなったことを見て「この変数の値が変わったんだ」と認識しやすくしてます。

この「ローカル」画面でプログラムの進行状態に伴い、自分が意図した値となっているかを確認することでバグを見つけ出すことができます。

ナナ

まず、覚えるべきは『ブレークポイント』『ステップオーバー』『ローカル画面』の3つですね。

これらはデバッガを扱う基本機能になります。

スポンサー

デバッグ技術 Level1:バグを見つける流れ実践編

ナナ

この章では普段、わたしが直接指導しているような感じで解説しますよ。このあたりはデバッグ手順は、初心者の方にはめちゃくちゃ役に立つ技術です。

絶対、覚えて損はないスキルです。

ここまでで習得した「デバッガ機能」を使って、どのような流れでプログラムの問題を見つけていくのかを実践的な手順で示していきましょう。

独学で頑張る人ほどオススメする『デバッガ』機能

『デバッガ』機能を正しく使ってプログラムを順に見ていけば、初心者が抱える9割くらいの問題は解決するでしょう。

つまり、本サイトのような独学で学ぶ初心者の方ほど、

『デバッガ』を使いこなすことで自分で問題が解決できる

のです!

それほど、この『デバッガ』は強力な機能なんです。

ただ、「デバッガを使うといいよ!」とアドバイスしても、「あぁ、はい…(デバッガって何?どうやって使うの…)」と手が止まってしまうんです。

そこで、実際に問題のあるプログラムを例に、どんな風にデバッガを使うのかを解説します。

問題を抱えたサンプルプログラム

簡単なプログラムを例に、次の課題でデバッガの使い方を覚えましょう。

課題:台形の面積を求め100以上か100未満でメッセージを変化する

  • 変数「up」を上底、「bottom」を下底、「height」を高さとして定義し、任意の数値を与えるものとする。
  • 台形の面積が100以上の場合は「面積が大きいです」、100未満の場合は「面積が小さいです」と表示せよ

出力期待結果

ケース①:up = 5, bottom = 10, height = 10 のとき

面積は小さいです

ケース②:up = 5, bottom = 15, height = 10 のとき

面積は大きいです

この課題を、次のようにプログラムしてみました。実はこのプログラムには「バグ」が潜んでいます。

#include <stdio.h>

int main(void)
{
	int up = 5;			//	上底
	int bottom = 10;	//	下底
	int height = 10;	//	高さ

	int area;			//	算出面積

	area = up + bottom * height / 2;

	if (area > 100)
	{
		printf("面積は大きいです");
	}
	else
	{
		printf("面積は小さいです");
	}

	return 0;
}
面積は小さいです

このプログラムの「バグ」をデバッガ機能を使って見つけていきましょう。

STEP1:プログラムが上手く動いていないことを自覚する

まずは、問題が起きていることを自覚しなければ「バグ」を取り除くことはできません。「バグ」は巧妙に隠れていますので、特定のケースでのみ問題が起こるといったことも珍しくありません。

先ほどの例はケース①の入力パターンであり、期待通りの出力結果が得られました。それでは、ケース②の入力パターンで動かしてみましょう。

#include <stdio.h>

int main(void)
{
	int up = 5;			//	上底
	int bottom = 15;	//	下底
	int height = 10;	//	高さ

	int area;			//	算出面積

	area = up + bottom * height / 2;

	if (area > 100)
	{
		printf("面積は大きいです");
	}
	else
	{
		printf("面積は小さいです");
	}

	return 0;
}
面積は小さいです

期待する結果としては「面積は大きいです」なのですが、「面積は小さいです」が表示されてしまいました。

これが「うまく動いていないことを自覚した」の瞬間です。

このように「バグ」が潜んでいないかを確認するために、様々な入力パターンを試すことで「バグ」の存在を明らかにしていくのです。

ナナ

今回はわたしが出力期待値を指定しているため、すぐに明確に間違っていることがわかりますね。

しかし、本来は皆さん自身で出力期待値を設計し、その通りになっているかを確認することになります。これを「テストケース」と呼びますよ。

STEP2:うまくいかない原因を調べる

「バグ」が潜んでいることがわかったら、次は原因を調べる作業に入りましょう。問題が起きているテストケースで原因を調べていきます。

ここからは『デバッガ機能』の出番です。初心者の方はまずは、プログラムの先頭に『ブレークポイント』を設定するようにしましょう。

『ブレークポイント』を設定したら「デバッグの開始」をしてデバッグの準備完了です。

ナナ

だんだん、デバッグのコツがわかってくると、原因がありそうな場所に『ブレークポイント』を設定し調べられるようになります。

慣れないうちは最初の行に設定しておきます。

次は、『ステップオーバー』でステップ実行を行いながら順にプログラムを検証します。プログラムを1行1行じっくりと吟味しながら、意図した動きになっているかをチェックします。

この時、『ローカル』画面を常に注視して、変数の値が狙い通りの値になっているのかをチェックしましょう。

初心者がよくやってしまうダメなことが「何も考えずにステップオーバーを連打する」ことです。これはやりがちですが、やっちゃダメです。

ナナ

このような初心者を何人も見てきましたが、「それ意味ないから連打しちゃダメ!」と毎回指導します。

デバッグとは問題を探す工程なんです。連打なんかしてたら問題を見落としてしまうに決まってるんですよ!

『ステップ実行』をする際にはポイントがあります。それは、次に処理されるプログラムの実行結果を予想しながら順に動かすことです。

皆さんが動かすその1行のプログラムは、必ず何らかの意図を持った処理なんです。その理想の動きと、『デバッガ』が突きつける事実を照らし合わせてプログラムを検証していきます。

それでは次のプログラムで『ステップ実行』を行い、実行が白色の行に差し掛かったとしましょう。

#include <stdio.h>

int main(void)
{
	int up = 5;			//	上底
	int bottom = 15;	//	下底
	int height = 10;	//	高さ

	int area;			//	算出面積

	area = up + bottom * height / 2;

	if (area > 100)
	{
		printf("面積は大きいです");
	}
	else
	{
		printf("面積は小さいです");
	}

	return 0;
}

この時に考えるべきことは

「台形の面積は上底と下底を足した20に、高さ10を掛けて2で割るのが正解だな。つまり、面積は100になれば合ってるよね」と、

「この結果になるべき!」と実行結果を予想した後で『ステップ実行』をします。

動かした結果、理想の「100」に対して、デバッガは「80」であるという事実を突きつけました。この理想と事実の違いこそがバグの発生地点です。

つまり、『ステップ実行』をするときは、常に「理想の結果」と「デバッガの事実」を比較しながら、違いが出てくる場所を見つけるのです。

ナナ

これは『デバッガ』を使うときの、すっごい大事なポイントを解説していますよ。

本当に初心者には役に立つ考え方なので理解できるまで読み直して、実際に実践して身に付けてください!

STEP3:正しく動くためにはどうしたらよいかを考え作る

原因がわかった次は、如何に「バグ」を取り除くかですね。

今回は台形の面積が正しく計算できていないわけです。算数の計算は加減算より乗除算の方が優先されるのでしたね。これはプログラムの世界でも同じです。

つまり、()を使って上底と下底の足し算を先に計算すればよいということですね。

#include <stdio.h>

int main(void)
{
	int up = 5;			//	上底
	int bottom = 15;	//	下底
	int height = 10;	//	高さ

	int area;			//	算出面積

	area = (up + bottom) * height / 2;

	if (area > 100)
	{
		printf("面積は大きいです");
	}
	else
	{
		printf("面積は小さいです");
	}

	return 0;
}
ナナ

実はこのプログラムにはもう一つバグが含まれています。皆さん、デバッガを使って原因を調べてみてくださいね。

デバッグ技術の「Level2」は『関数のデバッグ方法と脳内デバッグの鍛錬【初心者向け】』の記事で紹介しています。関数が学び終わった方は是非見ておくことをオススメします。