程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 在VC中編譯、運行程序的小知識點

在VC中編譯、運行程序的小知識點

編輯:關於C語言
 

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。

5、VC中缺省庫沖突的解決
VC的編譯器在編譯程序時有兩個習慣:
a、在從頭開始編譯時,將源文件名按字母排序後,依次處理;
b、一邊編譯一邊決定需要哪些缺省庫。
它的這些習慣有時會造成奇怪的編譯錯誤,例如項目中有兩個文件:
charutil.c
gbnni.cpp
其中gbnni.cpp用到了MFC庫。

它老兄當然是先處理charutil.c,然後覺得需要link一個C Runtime庫,根據項目設置選擇了LIBCMTD.lib。
然後又處理gbnni.cpp,因為要用MFC,又決定要link nafxcwd.lib。
最後link的時候,就會出現以下沖突:
nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj)
其實,如果先link了nafxcwd.lib,就沒有必要再link LIBCMTD.lib,也就不會產生沖突。

解決這類問題有兩個辦法。
a、讓項目的第一個文件包含MFC的頭文件,這樣編譯器就不會想到找C Runtime庫。這樣就要把c文件改成cpp了。
b、將需要link C Runtime庫的文件的名字改大一些,讓它排在後面。
使用IDE當然很方便,但既然使用了別人寫的工具,有時就不得不琢磨、遷就它的習性

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved