VC6 提供 6 種 CRT 組合:單/多執行緒 × 靜態/動態連結 × Debug/Release。對應的 .lib 檔案與 cl 選項如下:
| 模型 | 描述 | 連結庫 | 編譯選項 |
|---|---|---|---|
| ST | 單執行緒、靜態 | LIBC.LIB | /ML |
| MT | 多執行緒、靜態 | LIBCMT.LIB | /MT |
| DLL | 多執行緒、動態 | MSVCRT.LIB | /MD |
| XST | ST Debug 版 | LIBCD.LIB | /MLd |
| XMT | MT Debug 版 | LIBCMTD.LIB | /MTd |
| XDLL | DLL Debug 版 | MSVCRTD.LIB | /MDd |

安裝 VC6 時勾選「CRT Source Code」,即可在 VC98\CRT\SRC 目錄找到完整原始碼(含 stdio.h、string.h、C++ STL 標頭等)。面對成百上千檔案,如何切入?關鍵在於「連結器錯誤訊息」。
前文使用 /Zl(不帶預設庫)編譯 hello.c 並連結時,出現兩個 LNK2001 錯誤:
_printf:printf 函式實作符號_mainCRTStartup:程式真正入口點符號這兩個符號均定義於 LIBC.LIB 中。_printf 對應標準輸出函式,_mainCRTStartup 則是 EXE 實際進入點,負責 CRT 初始化後再呼叫使用者的 main()。
在 CRT\SRC 搜尋 mainCRTStartup,找到 crt0.c。其核心流程(簡化版):
int WINAPI mainCRTStartup() {
// 1. 初始化 CRT:堆疊、堆積、I/O、區域設定、引數解析...
_heap_init(); // 建立 CRT 堆積
_ioinit(); // 初始化標準串流 stdin/stdout/stderr
// 2. 取得命令列參數、環境變數
// 3. 呼叫使用者 main / wmain / WinMain / wWinMain
int ret = main(__argc, __argv, __envp);
// 4. 執行 atexit 登錄函式、關閉 I/O、釋放資源
exit(ret);
}
原始碼中大量使用 #ifdef 區分 Unicode/ANSI、Console/GUI、DLL/EXE 等情境,同一檔案透過巨集產生 wcrt0.c、wincrt0.c、wwincrt0.c 等多種入口常式。
mainCRTStartup,完成 CRT 初始化後才呼叫 main。main(略過 mainCRTStartup),printf 等函式在呼叫 _heap_alloc_base 時會因堆積未初始化而崩潰(下一篇將實測)。