【C++】デバッグのやり方とは?|Visual Studio・OutputDebugStringの使い方を解説

サムネイル画像 C++

今回は、C++での「デバッグのやり方」について解説していきます。

「エラーは出ていないのに、動きが変だ…」
「どの変数がおかしいのか見つけられない」
「OutputDebugStringAって何?どうやって使うの?」

こんな疑問はありませんか?

プログラムを書いていると、コンパイルエラーよりも「実行はできるけど結果がおかしい」という場面の方が意外と多いです。

この記事を読み終えると、あなたはC++での基本的なデバッグ方法から、OutputDebugStringA / W を使ったログ出力、日本語デバッグ時の注意点まで理解できるようになると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

デバッグとは?

デバッグとは、プログラムの不具合(バグ)を見つけて原因を調べ、修正する作業のことです。

たとえば、

  • 変数の値が想定と違う
  • if文の条件分岐が思った通りに動かない
  • ループが終わらない
  • 関数が呼ばれていると思ったら呼ばれていない

こういった問題を切り分けるのがデバッグです。

なぜデバッグが必要?

初心者のうちは、バグが起きるとすぐにコードを書き直したくなるかもしれません。

ですが、原因を特定せずに修正すると、別の場所を壊してしまうことがかなり多いです。どこで」「何が」「どうズレたのか」を確認するのが大切です。

DebugビルドとReleaseビルドの違いがまだ曖昧な方は、DebugとReleaseビルドの違いの記事も先に読んでおくと理解しやすいです。

まず覚えたい基本のデバッグ方法

C++でよく使うデバッグ方法は、主に次の3つです。

  • ブレークポイントを使って処理を止める
  • 変数の値を見る
  • ログを出力する

特にVisual Studioでは、ブレークポイントを置いて1行ずつ処理を追うだけでもかなり原因を見つけやすくなります。

ブレークポイントで止めて確認する

一番基本で強いのがブレークポイントです。

怪しい行にブレークポイントを置いて実行すると、その場所でプログラムが一時停止します。そこで、

  • 変数の値
  • 条件式の結果
  • どの関数から呼ばれてきたか

を確認できます。

とくに「このif文に入るはずなのに入らない」「このループで何回回っているのかわからない」といった場面ではかなり有効です。

std::coutでログを出す方法

手軽に確認したいだけなら、std::cout で変数や処理の通過点を表示する方法も便利です。

▼main.cpp

#include <iostream>

int main() {
    int hp = 100;
    int damage = 30;

    std::cout << "ダメージ前 HP: " << hp << "\n";

    hp -= damage;

    std::cout << "ダメージ後 HP: " << hp << "\n";

    return 0;
}

実行結果

ダメージ前 HP: 100
ダメージ後 HP: 70

この方法はシンプルですが、コンソールアプリ以外では見づらかったり、ログが大量になると追いにくくなったりします。

OutputDebugStringAとは?

OutputDebugStringA は、文字列をデバッガに送るためのWindows APIです。利用するには Windows.h をインクルードします。デバッガが接続されていない場合は、何も表示されないことがあります。これはMicrosoft公式ドキュメントでも説明されています。Microsoft公式

コンソールに出すのではなく、Visual Studio の「出力」ウィンドウにログを流したい時に便利です。

▼main.cpp

#include <Windows.h>

int main() {
    OutputDebugStringA("デバッグ開始\n");
    OutputDebugStringA("enemy hp = 80\n");

    return 0;
}

実行結果

Visual Studio の「出力」ウィンドウに以下が表示されます
デバッグ開始
enemy hp = 80

コンソールを汚したくない時や、ゲームループ中の内部状態を見たい時にかなり便利です。

OutputDebugStringAで変数を出力する方法

OutputDebugStringA はC文字列を渡す必要があるので、数値をそのまま渡すことはできません。文字列に組み立ててから渡します。

▼main.cpp

#include <Windows.h>
#include <string>

int main() {
    int hp = 75;

    std::string msg = "player hp = " + std::to_string(hp) + "\n";
    OutputDebugStringA(msg.c_str());

    return 0;
}

実行結果

Visual Studio の「出力」ウィンドウに以下が表示されます
player hp = 75

この形にしておくと、HP・座標・フラグなど、いろいろな値を簡単に出せます。

日本語のデバッグ文字列を出したい時は?

日本語を含むデバッグ文字列を扱う場合、OutputDebugStringA より OutputDebugStringW の方が安全です。

Microsoft公式ドキュメントでは、OutputDebugStringW はUnicode版として案内されており、文字列変換の都合で一部のUnicode文字が正しく表示されない可能性にも触れています。日本語を扱うなら、ANSI版よりUnicode版を使う方がトラブルを減らしやすいです。Microsoft公式

▼main.cpp

#include <Windows.h>

int main() {
    OutputDebugStringW(L"日本語デバッグ出力です\n");
    OutputDebugStringW(L"プレイヤーのHPを確認中\n");

    return 0;
}

実行結果

Visual Studio の「出力」ウィンドウに以下が表示されます
日本語デバッグ出力です
プレイヤーのHPを確認中

L"..." はワイド文字列リテラルです。日本語のログをそのまま扱いたいなら、まずはこちらを覚えておくと便利です。

デバッグをしやすくするコツ

  • 怪しい場所の直前と直後にログを入れる
  • 変数1つだけでなく、条件に関係する値をまとめて出す
  • ログを見たらすぐ消すのではなく、原因が確定するまで残す
  • Debugビルドで確認してからReleaseも試す

特にループや入力処理は、どのタイミングで値が変わったのかが大事です。ゲーム入力まわりのデバッグ例を見たい方は、キーボード入力の記事や、WinMMでコントローラー入力を取得する記事も参考になります。

【重要】私が実際にデバッグで困った体験談

私も自主制作のゲームで、プレイヤーの当たり判定がたまにズレるバグにかなり悩んだことがあります。

最初は描画処理がおかしいのかと思っていたのですが、ブレークポイントで止めて座標を確認すると、描画ではなく「更新順」が原因でした。さらに、ゲームループの中で毎フレームの座標を OutputDebugStringA で出すようにしたところ、特定のフレームだけ古い座標を参照していることが見えてきました。

また、日本語で「プレイヤー更新前」「プレイヤー更新後」と出したくて最初は OutputDebugStringA を使っていたのですが、環境によっては文字化け気味になって見づらかったので、最終的には OutputDebugStringW に切り替えました。

この時に実感したのは、「止めて見る」「値を出す」「日本語で意味が分かるログにする」の3つを意識すると、原因特定がかなり速くなるということでした。

デバッグ時のよくある失敗例と対処法

  • 怪しい場所ではなく、関係ない場所にログを出してしまう
    まずは「入力」「更新」「描画」など、処理の区切りごとに絞って確認しましょう。
  • OutputDebugStringA で日本語をそのまま扱ってしまう
    英数字中心ならA版でも動きますが、日本語ログは OutputDebugStringW の方が安全です。
  • Releaseビルドでだけ確認してしまう
    普段のデバッグはまずDebugビルドで行う方が追いやすいです。

どの方法を使うべき?

  • 原因の場所を特定したい → ブレークポイント
  • 手早く値を見たい → std::cout
  • Visual Studio の出力ウィンドウに流したい → OutputDebugStringA / W
  • 日本語ログを見やすく出したい → OutputDebugStringW

まとめ

  • デバッグは、バグの原因を調べて直すための作業
  • まずはブレークポイント・変数確認・ログ出力を覚えるのが大切
  • std::cout は手軽、OutputDebugStringA / W はVisual Studioの出力ウィンドウ向き
  • 日本語デバッグは OutputDebugStringW を使う方が安全
  • 原因を勘で直すのではなく、値を見て切り分けることが一番大事

デバッグは最初こそ面倒に感じますが、慣れてくると「どこで壊れたか」をかなり早く見つけられるようになります。

特にゲーム制作やツール制作では、ログの出し方を知っているだけで作業効率がかなり変わるので、早めに慣れておくのがおすすめです。

ここまで読んでくださり、ありがとうございました。

関連記事