在VC中編譯、運行程式的小知識點
1、Run-Time Library
Run-Time Library是編譯器提供的標準庫,提供一些基本的庫函數和系統調用。
我們一般使用的Run-Time Library是C Run-Time Libraries。當然也有Standard C++ libraries。
C Run-Time Libraries實現ANSI C的標準庫。VC安裝目錄的CRT目錄有C Run-Time庫的大部分原始碼。
C Run-Time Libraries有靜態庫版本,也有動態連結程式庫版本;有單線程版本,也有多線程版本;還有調試和非調試版本。
可以在"project"-"settings"-"C/C++"-"Code Generation"中選擇Run-Time Library的版本。
動態連結程式庫版本:
/MD Multithreaded DLL 使用匯入庫MSVCRT.LIB
/MDd Debug Multithreaded DLL 使用匯入庫MSVCRTD.LIB
靜態庫版本:
/ML Single-Threaded 使用靜態庫LIBC.LIB
/MLd Debug Single-Threaded 使用靜態庫LIBCD.LIB
/MT Multithreaded 使用靜態庫LIBCMT.LIB
/MTd Debug Multithreaded 使用靜態庫LIBCMTD.LIB
C Run-Time Library的標準io部分與作業系統的關係很密切,在Windows上,CRT的io部分代碼只是一個封裝,底層要用到作業系統核心kernel32.dll中的函數,在編譯時間使用匯入庫kernel32.lib。這也就是為什麼在嵌入式環境中,我們一般不能直接使用C標準庫。
在Linux環境當然也有C標準庫,例如:
ld -o output /lib/crt0.o hello.o -lc
參數"-lc"就是在引用C標準庫libc.a。猜一猜"-lm"引用哪個庫檔案?
2、常見的編譯參數
VC建立項目時總會定義"Win32"。控制台程式會定義"_CONSOLE",否則會定義"_WINDOWS"。Debug版定義"_DEBUG",Release版定義"NDEBUG"
與MFC DLL有關的編譯常數包括:
_WINDLL 表示要做一個用到MFC的DLL
_USRDLL 表示做一個使用者DLL(相對MFC擴充DLL而言)
_AFXDLL 表示使用MFC動態連結程式庫
_AFXEXT 表示要做一個MFC擴充DLL
所以:
Regular, statically linked to MFC _WINDLL,_USRDLL
Regular, using the shared MFC DLL _WINDLL,_USRDLL,_AFXDLL
Extension DLL _WINDLL,_AFXDLL,_AFXEXT
CL.EXE編譯所有源檔案,LINK.EXE連結EXE和DLL,LIB.EXE產生靜態庫。
3、subsystem和可執行檔的啟動
LINK的時候需要指定/subsystem,這個連結選項告訴Windows如何運行可執行檔。
控制台程式是/subsystem:"console"
其它程式一般都是/subsystem:"windows "
將 subsystem 選成"console"後,Windows在進入可執行檔的代碼前(如mainCRTStartup),就會產生一個控制台視窗。
如果選擇"windows",作業系統就不產生console視窗,該類型應用程式的視窗由使用者自己建立。
可執行檔都有一個Entry Point,LINK時可以用/entry指定。預設情況下,如果subsystem是“console”,Entry Point是 mainCRTStartup(ANSI)或wmainCRTStartuup(UNICODE),即:
/subsystem:"console" /entry:"mainCRTStartup" (ANSI)
/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)
mainCRTStartup 或 wmainCRTStartuup 會調用main或wmain。
值得一提的是,在進入應用程式的Entry Point前,Windows的裝載器已經做過C變數的初始化,有初值的全域變數擁有了它們的初值,沒有初值的變數被設為0。
如果subsystem是“windows”,Entry Point是WinMain(ANSI)或wWinMain(UINCODE),即:
/subsystem:"windows" /entry:"WinMainCRTStartup" (ANSI)
/sbusystem:"windows" /entry:"wWinMainCRTStartup" (UINCODE)
WinMainCRTStartup 或 wWinMainCRTStartup 會調用 WinMain 或 wWinMain。
如果使用MFC架構,WinMain也會被埋藏在MFC庫中(APPMODUL.CPP):
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
"_t"是一個宏,對於ANSI版本,"_tWinMain"就是"WinMain";對於UINCODE版本,"_tWinMain"就是"wWinMain"。
全域C++對象的建構函式是在什麼地方調用的?答案是在進入應用程式的Entry Point後,在調用main函數前的初始化操作中。所以MFC的theApp的建構函式是在_tWinMain之前調用的。
4、不顯示Console視窗的Console程式
在預設情況下/subsystem 和/entry開關是匹配的,也就是:
"console"對應"mainCRTStartup"或者"wmainCRTStartup"
"windows"對應"WinMain"或者"wWinMain"
我們可以通過手動修改的方法使他們不匹配。例如:
#include "windows.h"
#pragma comment( linker, "/subsystem:/"windows/" /entry:/"mainCRTStartup/"" ) // 設定入口地址
void main(void)
{
MessageBox(NULL, "hello", "Notice", MB_OK);
}
這個Console程式就不會顯示Console視窗。如果選/MLd的話,這個程式只需要連結LIBCD.LIB user32.lib kernel32.lib。