在VC中編譯、運(yùn)行程序的小知識(shí)點(diǎn)
1、Run-Time Library
Run-Time Library是編譯器提供的標(biāo)準(zhǔn)庫(kù),提供一些基本的庫(kù)函數(shù)和系統(tǒng)調(diào)用。
我們一般使用的Run-Time Library是C Run-Time Libraries。當(dāng)然也有Standard C++ libraries。
C Run-Time Libraries實(shí)現(xiàn)ANSI C的標(biāo)準(zhǔn)庫(kù)。VC安裝目錄的CRT目錄有C Run-Time庫(kù)的大部分源代碼。
C Run-Time Libraries有靜態(tài)庫(kù)版本,也有動(dòng)態(tài)鏈接庫(kù)版本;有單線程版本,也有多線程版本;還有調(diào)試和非調(diào)試版本。
可以在"project"-"settings"-"C/C++"-"Code Generation"中選擇Run-Time Library的版本。
動(dòng)態(tài)鏈接庫(kù)版本:
/MD Multithreaded DLL 使用導(dǎo)入庫(kù)MSVCRT.LIB
/MDd Debug Multithreaded DLL 使用導(dǎo)入庫(kù)MSVCRTD.LIB
靜態(tài)庫(kù)版本:
/ML Single-Threaded 使用靜態(tài)庫(kù)LIBC.LIB
/MLd Debug Single-Threaded 使用靜態(tài)庫(kù)LIBCD.LIB
/MT Multithreaded 使用靜態(tài)庫(kù)LIBCMT.LIB
/MTd Debug Multithreaded 使用靜態(tài)庫(kù)LIBCMTD.LIB
C Run-Time Library的標(biāo)準(zhǔn)io部分與操作系統(tǒng)的關(guān)系很密切,在Windows上,CRT的io部分代碼只是一個(gè)包裝,底層要用到操作系統(tǒng)內(nèi)核kernel32.dll中的函數(shù),在編譯時(shí)使用導(dǎo)入庫(kù)kernel32.lib。這也就是為什么在嵌入式環(huán)境中,我們一般不能直接使用C標(biāo)準(zhǔn)庫(kù)。
在Linux環(huán)境當(dāng)然也有C標(biāo)準(zhǔn)庫(kù),例如:
ld -o output /lib/crt0.o hello.o -lc
參數(shù)"-lc"就是在引用C標(biāo)準(zhǔn)庫(kù)libc.a。猜一猜"-lm"引用哪個(gè)庫(kù)文件?
2、常見的編譯參數(shù)
VC建立項(xiàng)目時(shí)總會(huì)定義"Win32"。控制臺(tái)程序會(huì)定義"_CONSOLE",否則會(huì)定義"_WINDOWS"。Debug版定義"_DEBUG",Release版定義"NDEBUG"
與MFC DLL有關(guān)的編譯常數(shù)包括:
_WINDLL 表示要做一個(gè)用到MFC的DLL
_USRDLL 表示做一個(gè)用戶DLL(相對(duì)MFC擴(kuò)展DLL而言)
_AFXDLL 表示使用MFC動(dòng)態(tài)鏈接庫(kù)
_AFXEXT 表示要做一個(gè)MFC擴(kuò)展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產(chǎn)生靜態(tài)庫(kù)。
3、subsystem和可執(zhí)行文件的啟動(dòng)
LINK的時(shí)候需要指定/subsystem,這個(gè)鏈接選項(xiàng)告訴Windows如何運(yùn)行可執(zhí)行文件。
控制臺(tái)程序是/subsystem:"console"
其它程序一般都是/subsystem:"windows "
將 subsystem 選成"console"后,Windows在進(jìn)入可執(zhí)行文件的代碼前(如mainCRTStartup),就會(huì)產(chǎn)生一個(gè)控制臺(tái)窗口。
如果選擇"windows",操作系統(tǒng)就不產(chǎn)生console窗口,該類型應(yīng)用程序的窗口由用戶自己創(chuàng)建。
可執(zhí)行文件都有一個(gè)Entry Point,LINK時(shí)可以用/entry指定。缺省情況下,如果subsystem是“console”,Entry Point是 mainCRTStartup(ANSI)或wmainCRTStartuup(UNICODE),即:
/subsystem:"console" /entry:"mainCRTStartup" (ANSI)
/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)
mainCRTStartup 或 wmainCRTStartuup 會(huì)調(diào)用main或wmain。
值得一提的是,在進(jìn)入應(yīng)用程序的Entry Point前,Windows的裝載器已經(jīng)做過(guò)C變量的初始化,有初值的全局變量擁有了它們的初值,沒(méi)有初值的變量被設(shè)為0。
如果subsystem是“windows”,Entry Point是WinMain(ANSI)或wWinMain(UINCODE),即:
/subsystem:"windows" /entry:"WinMainCRTStartup" (ANSI)
/注意用詞!usystem:"windows" /entry:"wWinMainCRTStartup" (UINCODE)
WinMainCRTStartup 或 wWinMainCRTStartup 會(huì)調(diào)用 WinMain 或 wWinMain。
如果使用MFC框架,WinMain也會(huì)被埋藏在MFC庫(kù)中(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"是一個(gè)宏,對(duì)于ANSI版本,"_tWinMain"就是"WinMain";對(duì)于UINCODE版本,"_tWinMain"就是"wWinMain"。
全局C++對(duì)象的構(gòu)造函數(shù)是在什么地方調(diào)用的?答案是在進(jìn)入應(yīng)用程序的Entry Point后,在調(diào)用main函數(shù)前的初始化操作中。所以MFC的theApp的構(gòu)造函數(shù)是在_tWinMain之前調(diào)用的。
4、不顯示Console窗口的Console程序
在默認(rèn)情況下/subsystem 和/entry開關(guān)是匹配的,也就是:
"console"對(duì)應(yīng)"mainCRTStartup"或者"wmainCRTStartup"
"windows"對(duì)應(yīng)"WinMain"或者"wWinMain"
我們可以通過(guò)手動(dòng)修改的方法使他們不匹配。例如:
#include "windows.h"
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) // 設(shè)置入口地址
void main(void)
{
MessageBox(NULL, "hello", "Notice", MB_OK);
}
這個(gè)Console程序就不會(huì)顯示Console窗口。如果選/MLd的話,這個(gè)程序只需要鏈接LIBCD.LIB user32.lib kernel32.lib。