【MFC/C++】Assertion failed!の読み方:assert()とDebug Assertion Failed!の見分け方

【MFC/C++】Assertion failed!の読み方:assert()とDebug Assertion Failed!の見分け方

Visual Studioで作ったアプリを動かしていると、「Microsoft Visual C++ Runtime Library」という題名で「Assertion failed!」と書かれたダイアログが出ることがあります。これはC/C++標準のassert()の条件が成り立たなかったときにCランタイムが出すもので、MFCのASSERTが出す「Debug Assertion Failed!」とは出どころが違います。どちらが出たのかの見分け方と、Program・File・Line・Expressionの読み方、3つのボタンを押したときの動きを順に書きます。


目次

Assertion failed!とDebug Assertion Failed!の見分け方

Visual Studio 2026、Debug/x64のMFCダイアログで、assert()、MFCのASSERT、Cランタイムのデバッグ用マクロ_ASSERTEをそれぞれ失敗させ、出たダイアログを比べました。3つとも題名は「Microsoft Visual C++ Runtime Library」で、ボタンも中止・再試行・無視の3つです。違いはダイアログの本文の1行目、Expressionの行、最後の行に出ました。

assert()MFCのASSERT_ASSERTE
1行目Assertion failed!Debug Assertion Failed!Debug Assertion Failed!
Expressionの行あるないある
最後の行(Press Retry to debug the application – JIT must be enabled)(Press Retry to debug the application)(Press Retry to debug the application)
ヘッダーassert.h(C++ではcassertも可)afx.hcrtdbg.h
消える条件NDEBUGが定義されているとき_DEBUGが定義されていないとき_DEBUGが定義されていないとき

1行目に「Debug」が付いていない「Assertion failed!」なら、assert()が出したダイアログです。「Debug Assertion Failed!」でExpressionの行が無ければ、MFCのASSERT(VERIFYもDebugではASSERTと同じ定義です)か、Cランタイムの_ASSERT(式の文字列を出さないマクロ)です。Expressionの行があれば、_ASSERTEのように式をダイアログに出すCランタイムのデバッグ用マクロです。どれだったかは、FileとLineでソースを開くと分かります。

assert()が失敗したときのダイアログ。1行目がAssertion failed!で、Program、File、Line、Expressionの行と、JIT must be enabledで終わる最後の行がある
assert()のダイアログ。1行目にDebugが付かず、Expressionの行がある
MFCのASSERTが失敗したときのダイアログ。1行目がDebug Assertion Failed!で、Program、File、Lineの行のあとにExpressionの行が無い
MFCのASSERTのダイアログ。Debug Assertion Failed!で、Expressionの行が無い
_ASSERTEが失敗したときのダイアログ。1行目がDebug Assertion Failed!で、Expression: nQuantity > 0の行がある
_ASSERTEのダイアログ。Debug Assertion Failed!で、Expressionの行がある

Assertion failed!のProgram・File・Line・Expressionの読み方

assert()のダイアログの4つの行は、次の意味です。

行出ているもの
Programassert()を呼んだコードが入っているexeまたはDLLのパス。長いときは先頭が「…」で省かれる
Fileassert()を書いたソースファイルのパス。長いときは途中が「…」で省かれる
Lineそのファイルの中で、assert()を書いた行の番号
Expressionassert()の括弧の中に書いた式。この式が0(false)になった

上の画像の例では、PriceTable.cppの30行目に書いたassert(nIndex >= 0 && nIndex < GetPriceItemCount())が成り立ちませんでした。nIndexが0より小さいか、品目の数以上だったということです。この例では品目が3つ(0〜2)のところに3が渡っていました。

FileとLineはassert()を書いた場所なので、ソースを開くとその行にassert()があります。ただ、その行は「ここに来るときはこうなっているはず」という前提を確かめているだけです。前提に合わない値は、その関数を呼んだ側から渡されていることがあります。この例でも、3は画面の入力欄の値で、ダイアログの処理がそのままGetUnitPriceに渡していました。値がどこから来たかは、次の「再試行」で呼び出し履歴を見て調べます。

コンソールアプリでは、assert()は標準エラーにAssertion failed: <式>, file <ファイル名>, line <行番号>の形で1行出してから、abort()を呼びます。このとき、assert()のダイアログの代わりにabort()のダイアログ(中止・再試行・無視)が出ることがあります。出力先は_set_error_modeで変えられます(assert Macro, _assert, _wassert)。


中止・再試行・無視を押したときの動き

assertの説明(上のリンク)には、ダイアログを出したときの3つのボタンの動きが表で書かれています。

ボタン動き
中止終了コード3でその場で終わる
再試行assertの中でデバッガーに入る。デバッガーを付けずに起動していた場合は、JIT(Just-In-Time)デバッグが有効なときだけデバッグできる
無視assertが無かったものとして続ける。前提が崩れたまま進むので、その後の動きは未定義になりうる

試したところ、assert()のダイアログでもMFCのASSERTのダイアログでも、中止を押すとプロセスの終了コードは3でした。

原因を調べるときは、Visual Studioのデバッガーで実行し(F5)、ダイアログが出たら再試行を押します。assert()の場合、デバッガーはassertの中(Cランタイムのコード)で止まります。呼び出し履歴(デバッグ > ウィンドウ > 呼び出し履歴)を上から見ていき、自分のソースファイルの名前が出てくる行をダブルクリックすると、assert()を書いた行に移ります。そのさらに下の行が、値を渡した呼び出し元です。

MFCのASSERTでは、再試行を押すとASSERTを書いた行でデバッガーが止まります。afx.hのASSERTは、ダイアログを出す関数が再試行で1を返したときに、その場でAfxDebugBreak(__debugbreak)を呼ぶ形に展開されるためです。呼び出し履歴の読み方はDebug Assertion Failed! の直し方に書いています。


ReleaseビルドでAssertion failed!が出たら

assert()は、NDEBUGが定義されていなければ、Debug版でもRelease版のCランタイムでも動きます(assertの説明)。MFCのASSERTと_ASSERTEは_DEBUGが定義されていないと消えるので(Diagnostic Services・_ASSERT, _ASSERTE, _ASSERT_EXPR Macros)、_DEBUGを定義していないRelease構成でビルドしたコードの中で出たダイアログはassert()のものです。ただし、_DEBUGが効くのはコンパイルするときなので、Debug構成でビルドされたDLLを一緒に使っていると、そのDLLの中のASSERTや_ASSERTEは動いたままです。Programの行がexeではなくDLLを指しているときは、そのDLLのビルド構成も見ます。

Releaseで出たときは、そのプロジェクトのプロパティの「C/C++ > プリプロセッサ > プリプロセッサの定義」にNDEBUGがあるかを見ます。NDEBUGは、assert.hを取り込む前に定義されている必要があります。プロジェクトに入れたほかのライブラリのソースでassert()が使われている場合は、そのライブラリのプロジェクトの設定も見ます。

NDEBUGを定義すると、assert()は括弧の中の式を評価しなくなります。assert(Init() == TRUE)のように、必要な処理をassert()の中に書いていると、Releaseではその処理ごと実行されません。


Debug Assertion Failed!でExpressionが無いとき(MFCのASSERT)

MFCのASSERTのダイアログには、Expressionの行がありません。afx.hのAfxAssertFailedLineが、ダイアログを出す_CrtDbgReportにファイル名と行番号だけを渡し、式の文字列を渡していないためです。Cランタイムの_ASSERTも同じく式を出しません。どの条件が成り立たなかったかは、FileとLineでソースを開いて、その行のASSERT(または_ASSERT)の括弧の中を読みます。FileがMFCの中のファイル(wincore.cppなど)を指しているときの追い方はDebug Assertion Failed! の直し方、自分のコードでのASSERTの使い方はASSERT(条件) でバグを早期発見するに書いています。


まとめ

  • 1行目が「Assertion failed!」ならassert()、「Debug Assertion Failed!」ならMFCのASSERTか、_ASSERTEなどCランタイムのデバッグ用マクロです。MFCのASSERTと_ASSERTのダイアログにはExpressionの行がありません。
  • FileとLineはassert()を書いた行、Expressionは成り立たなかった式です。前提に合わない値がどこから来たかは、デバッガーで実行して再試行を押し、呼び出し履歴でたどります。
  • assert()はNDEBUGが無ければReleaseでも動きます。Releaseで出たときは、プリプロセッサの定義にNDEBUGがあるかを見ます。Programの行がDLLを指しているときは、そのDLLのビルド構成も見ます。
目次