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.h | crtdbg.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でソースを開くと分かります。



Assertion failed!のProgram・File・Line・Expressionの読み方
assert()のダイアログの4つの行は、次の意味です。
| 行 | 出ているもの |
|---|---|
| Program | assert()を呼んだコードが入っているexeまたはDLLのパス。長いときは先頭が「…」で省かれる |
| File | assert()を書いたソースファイルのパス。長いときは途中が「…」で省かれる |
| Line | そのファイルの中で、assert()を書いた行の番号 |
| Expression | assert()の括弧の中に書いた式。この式が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のビルド構成も見ます。
