VC文件格式

VC文件格式

.opt 工程關於開發環境的參數文件。如工具條位置等信息;
.aps (AppStudio File),資源輔助文件,二進制格式,一般不用去管他.
.clw ClassWizard信息文件,實際上是INI文件的格式,有興趣可以研究一下.有時候ClassWizard出問題,手工修改CLW文件可以解決.如果此文件不存在的話,每次用ClassWizard的時候繪提示你是否重建.
.dsp (DeveloperStudio Project):項目文件,文本格式,不過不熟悉的話不要手工修改
.dsw(DeveloperStudio Workspace)是工作區文件,其他特點和DSP差不多.
.plg 是編譯信息文件,編譯時的error和warning信息文件(實際上是一個html文件),一般用處不大.在Tools->Options裏面有個選項可以控制這個文件的生成.
.hpj (Help Project)是生成幫助文件的工程,用microsfot Help Compiler可以處理.
.mdp (Microsoft DevStudio Project)是舊版本的項目文件,如果要打開此文件的話,會提示你是否轉換成新的DSP格式.
.bsc 是用於瀏覽項目信息的,如果用Source Brower的話就必須有這個文件.如果不用這個功能的話,可以在Project Options裏面去掉Generate Browse Info File,可以加快編譯速度.
.map 是執行文件的映像信息紀錄文件,除非對系統底層非常熟悉,這個文件一般用不着.
.pch (Pre-Compiled File)是預編譯文件,可以加快編譯速度,但是文件非常大.
.pdb (Program Database)記錄了程序有關的一些數據和調試信息,在調試的時候可能有用.
.exp 只有在編譯DLL的時候纔會生成,記錄了DLL文件中的一些信息.一般也沒什麼用. +.ncb 無編譯瀏覽文件(no compile browser)。當自動完成功能出問題時可以刪除此文件。build後會自動生成。
備份文件時,不必須的文件都可以刪除(尤其是ncb,opt,pch,這些文件常常很大)。

23 09, 2006

LNK2001錯誤探究[轉]

  學習VC++時經常會遇到鏈接錯誤LNK2001,該錯誤非常討厭,因爲對於編程者來說,最好改的錯誤莫過於編譯錯誤,而一般說來發生連接錯誤時,編譯都已通過。產生連接錯誤的原因非常多,尤其LNK2001錯誤,常常使人不明其所以然。如果不深入地學習和理解VC++,要想改正連接錯誤LNK2001非常困難。

  初學者在學習VC++的過程中,遇到的LNK2001錯誤的錯誤消息主要爲:

  unresolved external symbol “symbol”(不確定的外部“符號”)。

  如果連接程序不能在所有的庫和目標文件內找到所引用的函數、變量或標籤,將產生此錯誤消息。一般來說,發生錯誤的原因有兩個:一是所引用的函數、變量不存在、拼寫不正確或者使用錯誤;其次可能使用了不同版本的連接庫。

  以下是可能產生LNK2001錯誤的原因:

  一.由於編碼錯誤導致的LNK2001。

  1.不相匹配的程序代碼或模塊定義(.DEF)文件能導致LNK2001。例如,如果在C++ 源文件內聲明瞭一變量“var1”,卻試圖在另一文件內以變量“VAR1”訪問該變量,將發生該錯誤。

  2.如果使用的內聯函數是在.CPP文件內定義的,而不是在頭文件內定義將導致LNK2001錯誤。

  3.調用函數時如果所用的參數類型同函數聲明時的類型不符將會產生LNK2001。

  4.試圖從基類的構造函數或析構函數中調用虛擬函數時將會導致LNK2001。

  5.要注意函數和變量的可公用性,只有全局變量、函數是可公用的。

  靜態函數和靜態變量具有相同的使用範圍限制。當試圖從文件外部訪問任何沒有在該文件內聲明的靜態變量時將導致編譯錯誤或LNK2001。

  函數內聲明的變量(局部變量) 只能在該函數的範圍內使用。

  C++的全局常量只有靜態連接性能。這不同於C,如果試圖在C++的多個文件內使用全局變量也會產生LNK2001錯誤。一種解決的方法是需要時在頭文件中加入該常量的初始化代碼,並在.CPP文件中包含該頭文件;另一種方法是使用時給該變量賦以常數。

  二.由於編譯和鏈接的設置而造成的LNK2001

  1.如果編譯時使用的是/NOD(/NODEFAULTLIB)選項,程序所需要的運行庫和MFC庫在連接時由編譯器寫入目標文件模塊, 但除非在文件中明確包含這些庫名,否則這些庫不會被鏈接進工程文件。在這種情況下使用/NOD將導致錯誤LNK2001。

  2.如果沒有爲wWinMainCRTStartup設定程序入口,在使用Unicode和MFC時將得到“unresolved external on _WinMain@16”的LNK2001錯誤信息。

  3.使用/MD選項編譯時,既然所有的運行庫都被保留在動態鏈接庫之內,源文件中對“func”的引用,在目標文件裏即對“_imp__func” 的引用。

  如果試圖使用靜態庫LIBC.LIB或LIBCMT.LIB進行連接,將在__imp__func上發生LNK2001;如果不使用/MD選項編譯,在使用MSVCxx.LIB連接時也會發生LNK2001。

  4.使用/ML選項編譯時,如用LIBCMT.LIB鏈接會在_errno上發生LNK2001。

  5.當編譯調試版的應用程序時,如果採用發行版模態庫進行連接也會產生LNK2001;同樣,使用調試版模態庫連接發行版應用程序時也會產生相同的問題。

  6.不同版本的庫和編譯器的混合使用也能產生問題,因爲新版的庫裏可能包含早先的版本沒有的符號和說明。

  7.在不同的模塊使用內聯和非內聯的編譯選項能夠導致LNK2001。如果創建C++庫時打開了函數內聯(/Ob1或/Ob2),但是在描述該函數的相應頭文件裏卻關閉了函數內聯(沒有inline關鍵字),這時將得到該錯誤信息。爲避免該問題的發生,應該在相應的頭文件中用inline關鍵字標誌內聯函數。

  8.不正確的/SUBSYSTEM或/ENTRY設置也能導致LNK2001。

  其實,產生LNK2001的原因還有很多,以上的原因只是一部分而已,對初學者來說這些就夠理解一陣子了。但是,分析錯誤原因的目的是爲了避免錯誤的發生。LNK2001錯誤雖然比較困難,但是隻要注意到了上述問題,還是能夠避免和予以解決的。


23 09, 2006

VC++6.0編譯器詳解

prettybird 發表於 2005-12-1 15:49:00
大家可能一直在用VC開發軟件,但是對於這個編譯器卻未必很瞭解。原因是多方面的。大多數情況下,我們只停留在“使用”它,而不會想去“瞭解”它。因爲它只是一個工具,我們寧可把更多的精力放在C++語言和軟件設計上。我們習慣於這樣一種“模式”:建立一個項目,然後寫代碼,然後編譯,反反覆覆調試。但是,所謂:“公欲善其事,必先利其器”。如果我們精於VC開發環境,我們是不是能夠做得更加遊刃有餘呢?

  閒話少說。我們先來看一下VC的處理流程,大致分爲兩步:編譯和連接。源文件通過編譯生成了.obj文件;所有.obj文件和.lib文件通過連接生成.exe文件或.dll文件。下面,我們分別討論這兩個步驟的一些細節。


  編譯參數的設置。主要通過VC的菜單項Project->Settings->C/C++頁來完成。我們可以看到這一頁的最下面Project Options中的內容,一般如下:

/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_M
BCS" /Fp"Debug/WritingDlgTest.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c

  各個參數代表的意義,可以參考Msdn。比如/nologo表示編譯時不在輸出窗口顯示這些設置(我們可以把這個參數去掉來看看效果)等等。一般我們不會直接修改這些設置,而是通過這一頁最上面的Category中的各項來完成。

  1) General:一些總體設置。Warning level用來控制警告信息,其中Level 1是最嚴重的級別;Warnings as errors將警告信息當作錯誤處理;Optimizations是代碼優化,可以在Category的Optimizations項中進行更細的設置;Generate browse info用以生成.sbr文件,記錄類、變量等符號信息,可以在Category的Listing Files項中進行更多的設置。Debug info,生成調試信息:None,不產生任何調試信息(編譯比較快);Line Numbers Only,僅生成全局的和外部符號的調試信息到.OBJ文件或.EXE文件,減小目標文件的尺寸;C 7.0- Compatible,記錄調試器用到的所有符號信息到.OBJ文件和.EXE文件;Program Database,創建.PDB文件記錄所有調試信息;Program Database for "Edit & Continue",創建.PDB文件記錄所有調試信息,並且支持調試時編輯。

  2) C++ Language:pointer_to_member representation用來設置類定義/引用的先後關係,一般爲Best-Case Always表示在引用類之前該類肯定已經定義了;Enable Exception Handling,進行同步的異常處理;Enable Run-Time Type Information迫使編譯器增加代碼在運行時進行對象類型檢查;Disable Construction Displacements,設置類構造/析構函數調用虛函數問題。

  3) Code Generation:Processor表示代碼指令優化,可以爲80386、80486、Pentium、Pentium Pro,或者Blend表示混合以上各種優化。Use run-time library用以指定程序運行時使用的運行時庫(單線程或多線程,Debug版本或Release版本),有一個原則就是,一個進程不要同時使用幾個版本的運行時庫。Single-Threaded,靜態連接LIBC.LIB庫;Debug Single-Threaded,靜態連接LIBCD.LIB庫;Multithreaded,靜態連接LIBCMT.LIB庫;Debug Multithreaded,靜態連接LIBCMTD.LIB庫;Multithreaded DLL,動態連接MSVCRT.DLL庫;Debug Multithreaded DLL,動態連接MSVCRTD.DLL庫。連接了單線程庫就不支持多線程調用,連接了多線程庫就要求創建多線程的應用程序。

  Calling convention可以用來設定調用約定,有三種:__cdecl、__fastcall和__stdcall。各種調用約定的主要區別在於,函數調用時,函數的參數是從左到右壓入堆棧還是從右到左壓入堆棧;在函數返回時,由函數的調用者來清理壓入堆棧的參數還是由函數本身來清理;以及在編譯時對函數名進行的命名修飾(可以通過Listing Files看到各種命名修飾方式)。Struct member alignment用以指定數據結構中的成員變量在內存中是按幾字節對齊的,根據計算機數據總線的位數,不同的對齊方式存取數據的速度不一樣。這個參數對數據包網絡傳輸等應用尤爲重要,不是存取速度問題,而是數據位的精確定義問題,一般在程序中使用#pragma pack來指定。

  4) Customize:Disable Language Extensions,表示不使用微軟爲標準C做的語言擴展;Eliminate Duplicate Strings,主要用於字符串優化(將字符串放到緩充池裏以節省空間),使用這個參數,使得

char *sBuffer = "This is a character buffer";

char *tBuffer = "This is a character buffer";


  sBuffer和tBuffer指向的是同一塊內存空間;Enable Function-Level Linking ,告訴編譯器將各個函數按打包格式編譯;Enables minimal rebuild,通過保存關聯信息到.IDB文件,使編譯器只對最新類定義改動過的源文件進行重編譯,提高編譯速度;Enable Incremental Compilation,同樣通過.IDB文件保存的信息,只重編譯最新改動過的函數;Suppress Startup Banner and Information Messages,用以控制參數是否在output窗口輸出。

  5) Listing Files:Generate browse info的功能上面已經提到過。這裏可以進行更多的設置。Exclude Local Variables from Browse Info表示是否將局部變量的信息放到.SBR文件中。Listing file type可以設置生成的列表信息文件的內容:Assembly-Only Listing僅生成彙編代碼文件(.ASM擴展名);Assembly With Machine Code生成機器代碼和彙編代碼文件(.COD擴展名);Assembly With Source Code生成源代碼和彙編代碼文件(.ASM擴展名);Assembly, Machine Code,and Source生成機器碼、源代碼和彙編代碼文件(.COD擴展名)。Listing file name爲生成的信息文件的路徑,一般爲Debug或Release目錄下,生成的文件名自動取源文件的文件名。

  6) Optimizations:代碼優化設置。可以選擇Maximize Speed生成最快速的代碼,或Minimize Size生成最小尺寸的程序,或者Customize定製優化。定製的內容包括:

  Assume No Aliasing,不使用別名(提高速度);

  Assume Aliasing Across Function Calls,僅函數內部不使用別名;

  Global Optimizations,全局優化,比如經常用到的變量使用寄存器保存,或者循環內的計算優化,如

i = -100;

while( i < 0 ){ i += x + y;}


  會被優化爲


i = -100;
t = x + y;
while( i < 0 ){i += t;}
Generate Intrinsic Functions,使用內部函數替換一些函數調用(提高速度);
Improve Float Consistency,浮點運算方面的優化;
Favor Small Code,程序(exe或dll)尺寸優化優先於代碼速度優化;
Favor Fast Code,程序(exe或dll)代碼速度優化優先於尺寸優化;
Frame-Pointer Omission,不使用幀指針,以提高函數調用速度;
Full Optimization,組合了幾種參數,以生成最快的程序代碼。


  Inline function expansion,內聯函數擴展的三種優化(使用內聯可以節省函數調用的開銷,加快程序速度):Disable不使用內聯;Only __inline,僅函數定義前有inline或__inline標記使用內聯;Any Suitable,除了inline或__inline標記的函數外,編譯器“覺得”應該使用內聯的函數,都使用內聯。

  7) Precompiled Headers:預編譯頭文件的設置。使用預編譯可以提高重複編譯的速度。VC一般將一些公共的、不大變動的頭文件(比如afxwin.h等)集中放到stdafx.h中,這一部分代碼就不必每次都重新編譯(除非是Rebuild All)。

  8) Preprocessor:預編譯處理。可以定義/解除定義一些常量。Additional include directories,可以指定額外的包含目錄,一般是相對於本項目的目錄,如..Include。


  連接參數的設置。主要通過VC的菜單項Project->Settings->Link頁來完成。我們可以看到這一頁的最下面Project Options中的內容,一般如下:

/nologo /subsystem:windows /incremental:yes /pdb:"Debug/WritingDlgTest.pdb" /debug /machi
ne:I386 /out:"Debug/WritingDlgTest.exe" /pdbtype:sept

  下面我們分別來看一下Category中的各項設置。

  1) General:一些總體設置。可以設置生成的文件路徑、文件名;連接的庫文件;Generate debug info,生成Debug信息到.PDB文件(具體格式可以在Category->Debug中設置);Ignore All Default Libraries,放棄所有默認的庫連接;Link Incrementally,通過生成. ILK文件實現遞增式連接以提高後續連接速度,但一般這種方式下生成的文件(EXE或DLL)較大;Generate Mapfile,生成.MAP文件記錄模塊相關信息;Enable Profiling,這個參數通常與Generate Mapfile參數同時使用,而且如果產生Debug信息的話,不能用.PDB文件,而且必須用Microsoft Format。

  2) Customize:這裏可以進行使用程序數據庫文件的設置。Force File Output ,強制產生輸出文件(EXE或DLL);Print Progress Messages,可以將連接過程中的進度信息輸出到Output窗口。

  3) Debug:設置是否生成調試信息,以及調試信息的格式。格式可以有Microsoft Format、COFF Format(Common Object File Format)和Both Formats三種選擇;Separate Types,表示將Debug格式信息以獨立的.PDB文件存放,還是直接放在各個源文件的.PDB文件中。選中的話,表示採用後者的方式,這種方式調試啓動比較快。

  4) Input:這裏可以指定要連接的庫文件,放棄連接的庫文件。還可以增加額外的庫文件目錄,一般是相對於本項目的目錄,如..Lib。Force Symbol References,可以指定連接特定符號定義的庫。

  5) Output:Base Address可以改變程序默認的基地址(EXE文件默認爲0x400000,DLL默認爲x10000000),操作系統裝載一個程序時總是試着先從這個基地址開始。Entry-Point Symbol可以指定程序的入口地址,一般爲一個函數名(且必須採用__stdcall調用約定)。一般Win32的程序,EXE的入口爲WinMain,DLL的入口爲DllEntryPoint;最好讓連接器自動設置程序的入口點。默認情況下,通過一個C的運行時庫函數來實現:控制檯程序採用mainCRTStartup (或wmainCRTStartup)去調用程序的main (或wmain)函數;Windows程序採用WinMainCRTStartup (或 wWinMainCRTStartup)調用程序的WinMain (或 wWinMain,必須採用__stdcall調用約定);DLL採用_DllMainCRTStartup調用DllMain函數(必須採用__stdcall調用約定)。Stack allocations,用以設置程序使用的堆棧大小(請使用十進制),默認爲1兆字節。Version Information告訴連接器在EXE或DLL文件的開始部分放上版本號。


  值得注意的是,上面各個參數是大小寫敏感的;在參數後加上“-”表示該參數無效;各個參數值選項有“*”的表示爲該參數的默認值;可以使用頁右上角的“Reset”按鈕來恢復該頁的所有默認設置。


  其它一些參數設置

  1) Project->Settings->General,可以設置連接MFC庫的方式(靜態或動態)。如果是動態連接,在你的軟件發佈時不要忘了帶上MFC的DLL。

  2) Project->Settings->Debug,可以設置調試時運行的可執行文件,以及命令行參數等。

  3) Project->Settings->Custom Build,可以設置編譯/連接成功後自動執行一些操作。比較有用的是,寫COM時希望VC對編譯通過的COM文件自動註冊,可以如下設置:

   Description: Register COM

   Commands: regsvr32 /s /c $(TargetPath)

   echo regsvr32 exe.time > $(TargetDir)$(TargetName).trg

   Outputs: $(TargetDir)$(TargetName).trg

  4) Tools->Options->Directories,設置系統的Include、Library路徑。


  一些小竅門

  1) 有時候,你可能在編譯的時候,計算機突然非法關機了(可能某人不小心碰了電源或你的內存不穩定等原因)。當你重啓機器後打開剛纔的項目,重新進行編譯,發現VC會崩掉。你或許以爲你的VC編譯器壞了,其實不然(你試試編譯其它項目,還是好的!),你只要將項目的.ncb、.opt、.aps、.clw文件以及Debug、Release目錄下的所有文件都刪掉,然後重新編譯就行了。
  2) 如果你想與別人共享你的源代碼項目,但是把整個項目做拷貝又太大。你完全可以刪掉以下文件:.dsw、.ncb、.opt、.aps、.clw、. plg文件以及Debug、Release目錄下的所有文件。
  3) 當你的Workspace中包含多個Project的時候,你可能不能直觀地、一眼看出來哪個是當前項目。可以如下設置:Tools->Options->Format,然後在Category中選擇Workspace window,改變其默認的字體(比如設成Fixedsys)就行了。
  4) 如何給已有的Project改名字?將該Project關掉。然後以文本格式打開.dsp文件,替換原來的Project名字即可。
  5) VC6對類成員的智能提示功能很有用,但有時候會失靈。你可以先關掉項目,將.clw和.ncb刪掉,然後重新打開項目,點擊菜單項View->ClassWizard,在彈出的對話框中按一下“Add All”按鈕;重新Rebuild All。應該可以解決問題。


23 09, 2006

VC編譯語法解釋

VC- Project Setting-Debug-Project Option語法解釋
-優化-
/O1 最小化空間 minimize space
/Op[-] 改善浮點數一致性 improve floating-pt consistency
/O2 最大化速度 maximize speed
/Os 優選代碼空間 favor code space
/Oa 假設沒有別名 assume no aliasing
/Ot 優選代碼速度 favor code speed
/Ob 內聯展開(默認 n=0) inline expansion (default n=0)
/Ow 假設交叉函數別名 assume cross-function aliasing
/Od 禁用優化(默認值) disable optimizations (default)
/Ox 最大化選項。(/Ogityb2 /Gs) maximum opts. (/Ogityb1 /Gs)
/Og 啓用全局優化 enable global optimization
/Oy[-] 啓用框架指針省略 enable frame pointer omission
/Oi 啓用內建函數 enable intrinsic functions
-代碼生成-
/G3 爲 80386 進行優化 optimize for 80386
/G4 爲 80486 進行優化 optimize for 80486
/GR[-] 啓用 C++ RTTI enable C++ RTTI
/G5 爲 Pentium 進行優化 optimize for Pentium
/G6 爲 Pentium Pro 進行優化 optimize for Pentium Pro
/GX[-] 啓用 C++ 異常處理(與 /EHsc 相同) enable C++ EH (same as /EHsc)
/EHs 啓用同步 C++ 異常處理 enable synchronous C++ EH
/GD 爲 Windows DLL 進行優化 optimize for Windows DLL
/GB 爲混合模型進行優化(默認) optimize for blended model (default)
/EHa 啓用異步 C++ 異常處理 enable asynchronous C++ EH
/Gd __cdecl 調用約定 __cdecl calling convention
/EHc extern“C”默認爲 nothrow extern "C" defaults to nothrow
/Gr __fastcall 調用約定 __fastcall calling convention
/Gi[-] 啓用增量編譯 enable incremental compilation
/Gz __stdcall 調用約定 __stdcall calling convention
/Gm[-] 啓用最小重新生成 enable minimal rebuild
/GA 爲 Windows 應用程序進行優化 optimize for Windows Application
/Gf 啓用字符串池 enable string pooling
/QIfdiv[-] 啓用 Pentium FDIV 修復 enable Pentium FDIV fix
/GF 啓用只讀字符串池 enable read-only string pooling
/QI0f[-] 啓用 Pentium 0x0f 修復 enable Pentium 0x0f fix
/Gy 分隔鏈接器函數 separate functions for linker
/GZ 啓用運行時調試檢查 enable runtime debug checks
/Gh 啓用鉤子函數調用 enable hook function call
/Ge 對所有函數強制堆棧檢查 force stack checking for all funcs
/Gs[num] 禁用堆棧檢查調用 disable stack checking calls
-輸出文件-
/Fa[file] 命名程序集列表文件 name assembly listing file
/Fo 命名對象文件 name object file
/FA[sc] 配置程序集列表 configure assembly listing
/Fp 命名預編譯頭文件 name precompiled header file
/Fd[file] 命名 .PDB 文件 name .PDB file
/Fr[file] 命名源瀏覽器文件 name source browser file
/Fe 命名可執行文件 name executable file
/FR[file] 命名擴展 .SBR 文件 name extended .SBR file
/Fm[file] 命名映射文件 name map file
-預處理器-
/FI 命名強制包含文件 name forced include file
/C 不吸取註釋 don't strip comments
/U 移除預定義宏 remove predefined macro
/D{=|#} 定義宏 define macro
/u 移除所有預定義宏 remove all predefined macros
/E 將預處理定向到標準輸出 preprocess to stdout
/I 添加到包含文件的搜索路徑 add to include search path
/EP 將預處理定向到標準輸出,不要帶行號 preprocess to stdout, no #line
/X 忽略“標準位置” ignore "standard places"
/P 預處理到文件 preprocess to file
-語言-
/Zi 啓用調試信息 enable debugging information
/Zl 忽略 .OBJ 中的默認庫名 omit default library name in .OBJ
/ZI 啓用調試信息的“編輯並繼續”功能 enable Edit and Continue debug info
/Zg 生成函數原型 generate function prototypes
/Z7 啓用舊式調試信息 enable old-style debug info
/Zs 只進行語法檢查 syntax check only
/Zd 僅要行號調試信息 line number debugging info only
/vd{0|1} 禁用/啓用 vtordisp disable/enable vtordisp
/Zp[n] 在 n 字節邊界上包裝結構 pack structs on n-byte boundary
/vm 指向成員的指針類型 type of pointers to members
/Za 禁用擴展(暗指 /Op) disable extensions (implies /Op)
/noBool 禁用“bool”關鍵字 disable "bool" keyword
/Ze 啓用擴展(默認) enable extensions (default)
- 雜項 -
/?, /help 打印此幫助消息 print this help message
/c 只編譯,不鏈接 compile only, no link
/W 設置警告等級(默認 n=1) set warning level (default n=1)
/H 最大化外部名稱長度 max external name length
/J 默認 char 類型是 unsigned default char type is unsigned
/nologo 取消顯示版權消息 suppress copyright message
/WX 將警告視爲錯誤 treat warnings as errors
/Tc 將文件編譯爲 .c compile file as .c
/Yc[file] 創建 .PCH 文件 create .PCH file
/Tp 將文件編譯爲 .cpp compile file as .cpp
/Yd 將調試信息放在每個 .OBJ 中 put debug info in every .OBJ
/TC 將所有文件編譯爲 .c compile all files as .c
/TP 將所有文件編譯爲 .cpp compile all files as .cpp
/Yu[file] 使用 .PCH 文件 use .PCH file
/V 設置版本字符串 set version string
/YX[file] 自動的 .PCH 文件 automatic .PCH
/w 禁用所有警告 disable all warnings
/Zm 最大內存分配(默認爲 %) max memory alloc (% of default)

-鏈接-
/MD 與 MSVCRT.LIB 鏈接 link with MSVCRT.LIB
/MDd 與 MSVCRTD.LIB 調試庫鏈接 link with MSVCRTD.LIB debug lib
/ML 與 LIBC.LIB 鏈接 link with LIBC.LIB
/MLd 與 LIBCD.LIB 調試庫鏈接 link with LIBCD.LIB debug lib
/MT 與 LIBCMT.LIB 鏈接 link with LIBCMT.LIB
/MTd 與 LIBCMTD.LIB 調試庫鏈接 link with LIBCMTD.LIB debug lib
/LD 創建 .DLL Create .DLL
/F 設置堆棧大小 set stack size
/LDd 創建 .DLL 調試庫 Create .DLL debug libary
/link [鏈接器選項和庫] [linker options and libraries]


23 09, 2006

C++開發常用工具

開發環境
  ---->Turbo c

  DOS時代c語言開發的經典工具,目前適合兩類人使用:c語言beginner(尤其是學生一族),具有懷舊情節的專業人士:)

  ---->Visual C++ 6.0/7.0

  穩定而強大的IDE開發環境,具有豐富的調試功能,定製宏的功能也是其一大特色。Microsoft的經典之作,功能強大自不必言說。附帶的一些工具也很不錯,比如:Spy++。但編譯器較之同類,支持c++標準的程度不夠好,尤其6.0及以前的版本。

  ---->BCB

  Borland的C++ Builder是可以與VC匹敵的另一個功能強大的IDE,速度和穩定性稍遜,但對c++標準支持的程度較好。


  ---->Cygwin

  Windows平臺下的C++編譯器,基於gcc,又完全兼容Window特有的東西,比如對winsock的支持。從http://www.cygwin.com可以找到有關Cygwin的詳細信息。

  ---->Dev-c++

  Windows平臺下,一個類似VC、BCB的c++ IDE開發環境,屬於共享軟件。界面親切優雅,size也不大,其4.9.x版有中文語言支持,無需漢化。編譯器基於gcc,完全支持STL。但是對於規模較大的軟件項目,恐怕難以勝任。可以從:http://www.bloodshed.net/dev/devcpp.html找到有關Dev-c++的有關信息。

  ---->Source Insight

  有着和Dev c++一樣漂亮的界面,提供代碼的編輯和察看功能,具有豐富的語法加亮功能,可以像VC一樣自動彈出成員函數的提示,並具有快速方便的函數跳轉功能(但是跳轉速度似乎有點慢)。只是默認設置不適合時,需要仔細的調整和修改。無法查找經typedef之後的名字。

  輔助工具

  ---->Visual Assist

  VC IDE環境下的輔助編程工具,能識別各種關鍵字、函數、成員變量,自動給出tip,並自動更正大小寫錯誤,標示拼寫錯誤等。是VC開發的良好伴侶。

  ---->Understand for C++

  一款c/c++IDE編程的輔助工具,支持反向工程,代碼導向和一些統計功能,從http://www.scitools.com可以找到有關Understand for C++的詳細信息。

  程序編輯器

  ---->EditPlus

  一款很不錯的文本編輯軟件,功能強大卻又十分輕巧。支持不同語言的語法加亮,還有Project組織功能,具有豐富的自定義功能。通過設置User Tool,可以和其他語言編譯器結合,形成一個簡單的IDE。

  ---->Ultra Edit

  功能和EditPlus相當,通過腳本文件提供的配置功能可以定製編輯環境,但自6.0版以後一直沒多大變化,就編程而言,自定義設置沒有EditPlus方便。

  ---->EMACS

  公認的世界上功能最多,最複雜的文本編輯器,其實也可以當作程序員用的編輯器。

  ---->Visual Slick Edit

  一個功能強大的程序員用編輯器。最值得一提的是其定製功能,很好用,可以和EMACS相比。自帶了一套PCODE解釋器,用c的語法,還可以掛接動態庫。配合mingw一起使用很方便。從http://www.slickedit.com可以找到有關SlickEdit的詳細信息。

  ---->IQEdit

  全功能的程序員用編輯器,界面很漂亮,從http://pwksoftware.com可以找到有關IQEdit的詳細信息。

  UML/建模

  ---->Rational Rose

  強大的建模工具,早已“家喻戶曉”,功過自然不必多加評說了。從http://www.rationalsoftware.com.cn可以找到有關Rational Rose的詳細信息。

  ---->Visual Modeler

MS Visual Studio 6.0所附的小工具。屬於Rational和MicroSoft合作的戰略產品,是Rose的簡化版。
---->Visio

  Microsoft的建模工具,感覺更人性化一些,但功能沒有Rose多、強、專,支持正向的代碼生成,以及對代碼的反向工程。

  ---->Together

  另一款功能強大的建模工具,用java編寫而成,口碑不錯,不過速度稍稍慢了一些。從http://www.togethersoft.com可以找到有關Togather的詳細信息,另外,以下網址提供了一個有關Together的教程:    http://www.cc.puv.fi/~tka/kurssit/Tietojarjestelmien_suunnittelu/together/TCCGuide6

  ---->Visual UML

  支持多種語言類型,比如:VC、VB、DELPHI、CORBA IDL等,可以直接從UML設計圖生成代碼,簡單易用,完全適合Personal use,界面也很清爽。從http://www.visualObject.com可以找到有關Visual UML的詳細信息。

  ---->SmartDraw

  通用圖表製作軟件,可以用來製作組織機構圖、流程圖、統計圖表等。隨帶有圖庫,基本滿足製作各類圖表的需要。從http://www.smartdraw.com可以找到有關SmartDraw的詳細信息。

  ---->PlayCase

  國產面向對象的建模軟件,兼容UML和IDEF,輕量級軟件,只是界面看起來有點簡樸,乃是高展先生用Delphi完成的。

  版本控制

  ---->ClearCase

  Rational的版本控制管理軟件,功能強大,可以控制多種類型的文檔,甚至包括Word、Excel、PowerPoint文檔。但使用複雜,不易上手,且不是免費軟件。

  ---->CVS

  爲基於Web的分佈式協同開發提供了版本控制管理手段,且是免費軟件,可以通過腳本定製功能。但在權限控制方面功能相對較弱。

  ---->VSS

  微軟的版本控制管理工具,功能相對簡單,適合於小型團隊開發,將其整合到微軟的其他開發工具中,使用起來十分方便。

  ---->SourceOffSite

  微軟爲開發人員提供的遠程訪問VSS數據庫的工具,使數據庫得以遠程更新,以支持遠程辦公。其他的版本管理工具還包括:PVCS、VCS、RCS等。

  XML

  ---->expat

  用於讀取和處理XML文檔的c函數庫,最初是James Clark的個人作品,簡單輕巧,且速度快。但支持的編碼方式有限,最遺憾的是不支持中文。從http://expat.sourceforge.net可以找到有關expat的詳細信息。

  ---->xml4c

  IBM的XML Parser,用c++語言寫就,功能超級強大。號稱支持多達100種字符編碼,能夠支持中文,適合於大規模的xml應用。若只是很小範圍的應用,則非最佳選擇,畢竟,你需要“揹負”約12M左右的dll的沉重負擔。從http://www.alphaworks.ibm.com/tech/xml4c可以找到有關xml4c的詳細信息。

  ---->Xerces c++

  Apache的XML項目,同樣是c++實現,來源於IBM的xml4c,因此編程接口也是和xml4c一致的。但是目前只支持少數的字符編碼,如ASCII,UTF-8,UTF-16等,不能處理包含中文字符的XML文檔。從 http://xml.apache.org/xerces-c可以找到有關Xerces c++的詳細信息。

  測試

  ---->CppUnit

  一個c++的單元測試框架,可以通過派生測試類的方式,定製具體的測試方案。xUnit家族的一員,從JUnit移植而來,JUnit是Java語言的單元測試框架。從http://cppuint.sourceforge.net可以找到有關CppUint的詳細信息。

  ---->Rational ROBOT

  Rational的自動化測試工具,通過編寫腳本的方式提供自動化測試特性。其GUI方式的腳本錄製功能,有助於對GUI軟件進行功能測試;其VU方式的腳本錄製功能,有助於測試某些軟件的數據通訊功能。

  ---->Rational Purify

  同樣是Rational的自動化測試工具,不需要被測程序的源代碼,可以用來檢查內存訪問錯誤、Windows API調用錯誤等,以完成軟件的可靠性測試,屬於白盒測試。

  其他的Rational測試工具還包括:TestFactory、PureCoverage、TestManager等。

  日誌

  ---->log4cpp

  一個用於日誌記錄的c++函數庫,可以將內容以定製的方式記錄到不同的目的地,比如:文件、控制檯syslog等,同時還可以通過控制記錄級別來屏蔽掉某些無關記錄。從http://log4cpp.sourceforge.net可以找到有關log4cpp的詳細信息。

  註釋

  ---->Doc++

  註釋文檔生成工具,根據源程序中的文檔註釋,可以輸出TeX和HTML格式的文檔。除了支持c/c++語言外,還支持IDL和java。僅提供命令行使用方式。從http://docpp.sourceforge.net可以找到有關Doc++的詳細信息。

  ---->Doxygen

  註釋文檔生成工具,較之Doc++功能更爲齊全,可以生成包括HTML、PDF、RTF在內的多種格式的文檔,並有GUI界面,除了支持c/c++語言外,還支持IDL、java、PHP、c#等。從http://www.stack.nl/~dimitri/doxygen可以找到有關Doxygen的詳細信息。


23 09, 2006

_beginthread還是CreateThread[轉]

程序員對於Windows程序中應該用_beginthread還是CreateThread來創建線程,一直有所爭論。本文將從對CRT源代碼出發探討這個問題。

I. 起因

今天一個朋友問我程序中究竟應該使用_beginthread還是CreateThread,並且告訴我如果使用不當可能會有內存泄漏。其實我過去對這個問題也是一知半解,爲了對朋友負責,專門翻閱了一下VC的運行庫(CRT)源代碼,終於找到了答案。

II. CRT

CRT(C/C++ Runtime Library)是支持C/C++運行的一系列函數和代碼的總稱。雖然沒有一個很精確的定義,但是可以知道,你的main就是它負責調用的,你平時調用的諸如strlen、strtok、time、atoi之類的函數也是它提供的。我們以Microsoft Visual.NET 2003中所附帶的CRT爲例。假設你的.NET 2003安裝在C:Program FilesMicrosoft Visual Studio .NET 2003中,那麼CRT的源代碼就在C:Program FilesMicrosoft Visual Studio .NET 2003Vc7crtsrc中。既然有了這些實現的源代碼,我們就可以找到一切解釋了。

III. _beginthread/_endthread

這個函數究竟做了什麼呢?它的代碼在thread.c中。閱讀代碼,可以看到它最終也是通過CreateThread來創建線程的,主要區別在於,它先分配了一個_tiddata,並且調用了_initptd來初始化這個分配了的指針。而這個指針最後會被傳遞到CRT的線程包裝函數_threadstart中,在那裏會把這個指針作爲一個TLS(Thread Local Storage)保存起來。然後_threadstart會調用我們傳入的線程函數,並且在那個函數退出後調用_endthread。這裏也可以看到,_threadstart用一個__try/__except塊把我們的函數包了起來,並且在發生異常的時候,調用exit退出。(_threadstart和endthread的代碼都在thread.c中)
這個_tiddata是一個什麼樣的結構呢?它在mtdll.h中定義,它的成員被很多CRT函數所用到,譬如int _terrno,這是這個線程中的錯誤標誌;char* _token,strtok以來這個變量記錄跨函數調用的信息,...。
那麼_endthread又做了些什麼呢?除了調用浮點的清除代碼以外,它還調用了_freeptd來釋放和這個線程相關的tiddata。也就是說,在_beginthread裏面分配的這塊內存,以及在線程運行過程中其它CRT函數中分配並且記錄在這個內存結構中的內存,在這裏被釋放了。
通過上面的代碼,我們可以看到,如果我使用_beginthread函數創建了線程,它會爲我創建好CRT函數需要的一切,並且最後無需我操心,就可以把清除工作做得很好,可能唯一需要注意的就是,如果需要提前終止線程,最好是調用_endthread或者是返回,而不要調用ExitThread,因爲這可能造成內存釋放不完全。同時我們也可以看出,如果我們用CreateThread函數創建了線程,並且不對C運行庫進行調用(包括任何間接調用),就不必擔心什麼問題了。

IV. CreateThread和CRT

或許有人會說,我用CreateThread創建線程以後,我也調用了C運行庫函數,並且也使用ExitThread退出了,可是我的程序運行得好好的,既沒有因爲CRT沒有初始化而崩潰,也沒有因爲忘記調用_endthread而發生內存泄漏,這是爲什麼呢,讓我們繼續我們的CRT之旅。
假設我用CreateThread創建了一個線程,我調用strtok函數來進行字符串處理,這個函數肯定是需要某些額外的運行時支持的。strtok的源代碼在strtok.c中。從代碼可見,在多線程情況下,strtok的第一句有效代碼就是_ptiddata ptd = _getptd(),它通過這個來獲得當前的ptd。可是我們並沒有通過_beginthread來創建ptd,那麼一定是_getptd搗鬼了。打開tidtable.c,可以看到_getptd的實現,果然,它先嚐試獲得當前的ptd,如果不能,就重新創建一個,因此,後續的CRT調用就安全了。可是這塊ptd最終又是誰釋放的呢?打開dllcrt0.c,可以看到一個DllMain函數。在VC中,CRT既可以作爲一個動態鏈接庫和主程序鏈接,也可以作爲一個靜態庫和主程序鏈接,這個在Project Setting->Code Generations裏面可以選。當CRT作爲DLL鏈接到主程序時,DllMain就是CRT DLL的入口。Windows的DllMain可以由四種原因調用:Process Attach/Process Detach/Thread Attach/Thread Detach,最後一個,也就是當線程函數退出後但是線程還沒有銷燬前,會在這個線程的上下文中用Thread Detach調用DllMain,這裏,CRT做了一個_freeptd(NULL),也就是說,如果有ptd,就free掉。所以說,恰巧沒有發生內存泄漏是因爲你用的是動態鏈接的CRT。
於是我們得出了一個更精確的結論,如果我沒有使用那些會使用_getptd的CRT函數,使用CreateThread就是安全的。

V. 使用ptd的函數

那麼,究竟那些函數使用了_getptd呢?很多!在CRT目錄下搜索_getptd,你會發覺很多意想不到的函數都用到了它,除了strtok、rand這類需要保持狀態的,還有所有的字符串相關函數,因爲它們要用到ptd中的locale信息;所有的mbcs函數,因爲它們要用到ptd中的mbcs信息,...。

VI. 測試代碼

下面是一段測試代碼(leaker中用到了atoi,它需要ptd):

#include <windows.h>
#include <process.h>
#include <iostream>
#include <CRTDBG.H>

volatile bool threadStarted = false;

void leaker()
{
std::cout << atoi( "0" ) << std::endl;
}

DWORD __stdcall CreateThreadFunc( LPVOID )
{
leaker();
threadStarted = false;
return 0;
}

DWORD __stdcall CreateThreadFuncWithEndThread( LPVOID )
{
leaker();
threadStarted = false;
_endthread();
return 0;
}

void __cdecl beginThreadFunc( LPVOID )
{
leaker();
threadStarted = false;
}

int main()
{
for(;;)
{
while( threadStarted )
Sleep( 5 );
threadStarted = true;
// _beginthread( beginThreadFunc, 0, 0 );//1
CreateThread( NULL, 0, CreateThreadFunc, 0, 0, 0 );//2
// CreateThread( NULL, 0, CreateThreadFuncWithEndThread, 0, 0, 0 );//3
}
return 0;
}

如果你用VC的多線程+靜態鏈接CRT選項去編譯這個程序,並且嘗試打開1、2、3之中的一行,你會發覺只有2打開的情況下,程序纔會發生內存泄漏(可以在Task Manager裏面明顯的觀察到)。3之所以不會出現內存泄漏是因爲主動調用了_endthread。

VII. 總結

如果你使用了DLL方式鏈接的CRT庫,或者你只是一次性創建少量的線程,那麼你或許可以採取鴕鳥策略,忽視這個問題。上面一節代碼中第3種方法基於對CRT庫的瞭解,但是並不保證這是一個好的方法,因爲每一個版本的VC的CRT可能都會有些改變。看來,除非你的頭腦清晰到可以記住這一切,或者你可以不厭其煩的每調用一個C函數都查一下CRT代碼,否則總是使用_beginthread(或者它的兄弟_beginthreadex)是一個不錯的選擇。



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1862102 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章