本ドキュメントは、MSVC (Microsoft Visual C++) におけるランタイムライブラリのリンクモデル (/MT と /MD) の違いと、プロジェクト全体で適切なモデルを選択する重要性について説明します。
Windows 環境で C/C++ プログラムをビルドする際、ランタイムライブラリ (C ランタイム) のリンク方法を /MT (静的リンク) または /MD (動的リンク) で指定する必要があります。この選択は、実行ファイル (.exe)、静的ライブラリ (.lib)、動的リンクライブラリ (.dll) のすべてに影響し、誤った組み合わせは実行時のクラッシュやメモリ破壊の原因となります。
ランタイムライブラリは、プログラムの実行に必要な基本的な機能を提供するライブラリです。C ランタイムライブラリには、以下のような関数が含まれます。
malloc, free, new, delete)printf, scanf, fopen, fclose)strcpy, strlen, strcmp)sin, cos, sqrt)errno, stdin, stdout, stderr)すべての C/C++ プログラムは、これらの関数を直接的または間接的に使用するため、ランタイムライブラリが必要です。
MSVC では、ランタイムライブラリのリンク方法として /MT と /MD の2種類が提供されています。
C ランタイムライブラリのコードが、最終的な実行ファイルや DLL に直接埋め込まれます。
使用されるライブラリファイル
LIBCMT.lib (リリースビルド)LIBCMTD.lib (デバッグビルド、/MTd)特徴
vcruntime140.dll など) が不要C ランタイムライブラリのコードは実行ファイルには埋め込まれず、実行時に専用の DLL を動的にロードして使用します。
使用されるライブラリファイル
MSVCRT.lib (インポートライブラリ、リリースビルド)MSVCRTD.lib (インポートライブラリ、デバッグビルド、/MDd)実行時に必要な DLL
vcruntime140.dll (Visual Studio 2015 以降)msvcp140.dll (C++ 標準ライブラリ、C++ プログラムの場合)ucrtbase.dll (Universal CRT)特徴
以下の表は、/MT と /MD の主な違いをまとめたものです。
| 項目 | /MT (静的) | /MD (動的) |
|---|---|---|
| ランタイムコードの配置 | 実行ファイルに埋め込み | DLL として分離 |
| 実行ファイルのサイズ | 大きい | 小さい |
| 配布に必要なファイル | 実行ファイルのみ | 実行ファイル + ランタイム DLL |
| メモリ使用量 | 各モジュールが独立したコピーを持つため大きい | 複数モジュールで共有するため小さい |
| リンク時のライブラリファイル | LIBCMT.lib |
MSVCRT.lib (インポートライブラリ) |
| モジュール間でのランタイム共有 | 不可 (各モジュールが独立) | 可 (同じ DLL を共有) |
デバッグ用のランタイムは、追加の検査でバグを見つけやすくする特別版です。これをデバッグ CRT (Debug C Runtime) と呼びます。開発時の利用を想定しており、配布には使いません。
デバッグ CRT を実行ファイルや DLL に静的に取り込みます。
使用されるライブラリファイル
LIBCMTD.lib (デバッグビルド)特徴
/MT や /MD、/MDd との混在は不可ビルド設定例
CFLAGS := /W4 /Zi /TC /nologo /utf-8 /FS /MTd /Fd:$(OUTPUT_DIR)/$(TARGET_BASE).pdb /I$(WORKSPACE_FOLDER)/prod/calc/includeデバッグ CRT を実行時に DLL として読み込みます。
使用されるライブラリファイル
MSVCRTD.lib (インポートライブラリ、デバッグビルド)実行時に必要な DLL
vcruntime140d.dllmsvcp140d.dll (C++ の場合)ucrtbased.dll (Universal CRT のデバッグ版)特徴
/MT や /MD、/MTd との混在は不可ビルド設定例
CFLAGS := /W4 /Zi /TC /nologo /utf-8 /FS /MDd /Fd:$(OUTPUT_DIR)/$(TARGET_BASE).pdb /I$(WORKSPACE_FOLDER)/prod/calc/include/MTd または /MDd に統一し、リリースは /MT または /MD に統一するMSVCRTD などを含む形で出ることがある。必ず原因を直す異なるランタイムリンクモデル (/MT と /MD) でビルドされたモジュール (実行ファイル、静的ライブラリ、DLL) を混在させると、以下の深刻な問題が発生します。
/MT と /MD では、それぞれ独立したヒープマネージャーが使用されます。一方のヒープで確保したメモリを他方で解放しようとすると、クラッシュが発生します。
// libfoo.lib (/MT でビルド) 内の関数
char* create_buffer(void) {
return malloc(100); // /MT のヒープで確保
}
// main.exe (/MD でビルド) 内
int main(void) {
char* p = create_buffer();
free(p); // /MD のヒープで解放 → クラッシュ!
return 0;
}C ランタイムのグローバル変数 (例: errno, stdin, stdout) が、/MT と /MD のモジュールでそれぞれ別のインスタンスとして存在します。一方で設定した値が他方では反映されず、意図しない動作が発生します。
// libfoo.lib (/MT でビルド) 内の関数
void set_error(void) {
errno = EINVAL; // /MT の errno に設定
}
// main.exe (/MD でビルド) 内
int main(void) {
set_error();
printf("errno = %d\n", errno); // /MD の errno を参照 → 0 (変更されていない!)
return 0;
}異なるランタイムライブラリが混在している場合、リンカーは以下の警告を出力します。
LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
この警告は、プロジェクト内でランタイムリンクモデルが統一されていないことを示しています。この警告を無視すると、上記のようなクラッシュやメモリ破壊が発生する可能性があります。
プロジェクト全体で同じランタイムリンクモデルを使用する
すべての実行ファイル、静的ライブラリ、DLL を同じモデル (/MT または /MD) でビルドする必要があります。
Microsoft 公式ドキュメントでは、特に DLL を含むプロジェクトでは /MD の使用を推奨しています。理由は以下の通りです。
DLL との親和性
DLL は複数の実行ファイルから呼び出される可能性があります。/MD を使用することで、すべての DLL と実行ファイルが同じランタイムライブラリインスタンスを共有し、ヒープの不一致やグローバル変数の問題を回避できます。
メモリ効率
複数のモジュールが同じランタイムライブラリ DLL を共有するため、メモリ使用量が削減されます。
Microsoft の標準的な手法
Windows のシステム DLL や多くのサードパーティライブラリは /MD でビルドされているため、これに合わせることで互換性の問題を減らせます。
配布の手間
Microsoft は Visual C++ 再頒布可能パッケージ (Visual C++ Redistributable) を提供しており、ユーザーはこれをインストールすることで、すべての /MD アプリケーションに必要なランタイム DLL を一度に入手できます。
以下の条件を満たす場合は、/MT の選択も有効です。
単一の実行ファイルのみのプロジェクト
DLL を作成せず、単一の実行ファイルのみを配布する場合、/MT を使用することで配布が簡単になります (ランタイム DLL が不要)。
DLL 境界を越えたメモリ操作がない
DLL が整数や値型のみを受け渡し、ポインタの受け渡しやメモリの割り当て/解放を行わない場合、/MT でも問題は発生しません。
配布先の環境が限定的
配布先の環境が限定されており、ランタイム DLL のインストールが困難な場合、/MT を使用することで依存関係を削減できます。
本プロジェクト (doxygen-sample) では、以下の理由から /MD を採用しています。
DLL を含むプロジェクト
libcalc.dll を作成しており、これを add.exe がリンクしています。/MD を使用することで、DLL と exe が同じランタイムライブラリインスタンスを共有し、将来的な拡張 (ポインタの受け渡しなど) にも対応できます。
複雑なプロジェクトへの拡張性
将来、他のライブラリや機能を追加する際、/MD であれば互換性の問題が発生しにくくなります。
Microsoft の推奨に従う
標準的な Windows 開発の手法に従うことで、他の開発者やツールとの互換性を保ちます。
/MD でビルドされたプログラムを実行するには、Visual C++ 再頒布可能パッケージのインストールが必要です。
Microsoft の公式サイトから、使用している Visual Studio のバージョンに対応する再頒布可能パッケージをダウンロードします。
ダウンロードした再頒布可能パッケージ (例: vc_redist.x64.exe) を実行し、インストールします。これにより、以下の DLL がシステムにインストールされます。
vcruntime140.dllmsvcp140.dllucrtbase.dllインストール後、/MD でビルドされたプログラムが正常に実行できます。
/MDd でビルドしたプログラムは、vcruntime140d.dll、msvcp140d.dll、ucrtbased.dll が必要です。これらは Visual Studio または Build Tools に含まれ、Visual C++ 再頒布可能パッケージには含まれません/MTd は DLL 依存はありませんが、デバッグ CRT を静的に含むため配布には不向きですMSVC におけるランタイムライブラリのリンクモデルの選択は、プロジェクト全体の安定性と保守性に大きく影響します。
プロジェクト全体で統一する
すべてのモジュール (実行ファイル、静的ライブラリ、DLL) を同じモデルでビルドします。
複雑なプロジェクトでは /MD を推奨
DLL を含むプロジェクトや、将来的な拡張を想定する場合は、/MD (動的ランタイム) を使用します。
警告を無視しない
リンカー警告 LNK4098 が出力された場合は、ランタイムリンクモデルの不一致が発生しているため、必ず修正します。
配布時には再頒布可能パッケージが必要
/MD を使用する場合、ユーザーの環境に Visual C++ 再頒布可能パッケージがインストールされている必要があります。
本プロジェクトでは、すべてのコンポーネントを /MD でビルドすることで、ランタイムライブラリの統一を実現し、安定した動作を保証しています。
Microsoft Docs: C ランタイム ライブラリのリンク
https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/crt-library-features
Microsoft Docs: /MD、/MT、/LD (ランタイム ライブラリの使用)
https://learn.microsoft.com/ja-jp/cpp/build/reference/md-mt-ld-use-run-time-library
Microsoft Docs: 最新のサポートされる Visual C++ 再頒布可能パッケージのダウンロード
https://learn.microsoft.com/ja-jp/cpp/windows/latest-supported-vc-redist
Microsoft Docs: デバッグ CRT の概要
https://learn.microsoft.com/ja-jp/cpp/c-runtime-library/debug-versions-of-the-crt-library
Microsoft Docs: Visual C++ ファイルの再頒布(デバッグ版は再頒布不可)
https://learn.microsoft.com/ja-jp/cpp/windows/redistributing-visual-cpp-files