cl.exe和link.exe分別是visual studio 中的編譯器和鏈接器
配置屬性中的【c/c++】(設置編譯的一些選項) 和 【鏈接器】選項頁中,最後的那個命令行彙總了所有生效的設置,就是最終執行的命令行
配置屬性中的VC++ 目錄用於設置各個路徑,相當於設置環境變量PATH(搜可執行文件的路徑):
INCLUDE(搜include中文件的路徑)
LIBPATH(搜使用using 引入文件的路徑)
LIB(搜庫文件的路徑)
編譯過程:語法錯誤
鏈接過程:結構錯誤
運行過程:邏輯錯誤
目錄
編譯:
一般一個大的項目,修改完一個cpp以後,一般都先單獨編譯一下這個cpp,免得到整個工程生成的時候發現這個cpp的一些編譯錯誤。把光標停留在某個cpp的頁面中,然後【生成】--【編譯】,或直接ctrl+F7
編譯所做的事情:
對一個cpp進行預處理,將頭文件加載進來,並且將各種#define信息代入,生成一個獨立的編譯單元,然後進行編譯生成obj文件,一個cpp對應一個obj文件。
編譯過程,先生成彙編語言,再生成機器語言(即彙編指令對應的機器碼,cpu認識的指令)
1.常規設置
1)頭文件路徑設置
除了cpp定義的include,還有屬性中可以配置的附加包含目錄,以及VC++目錄中的包含目錄。
ps:【VC++目錄中的包含目錄】和【C/C++常規中的附加包含目錄】的區別:
包含目錄:修改了系統的include宏的值,是全局的;
附加包含目錄:用於當前項目,對其他項目沒有影響。————裏面填寫的相對路徑就是指的是相對於項目路徑的。如果有提示說找不到頭文件的,應該就是這裏配錯了,看看是不是單詞拼錯了,還是路徑層級弄錯了,還是壓根這裏就沒有配置。
(同理庫目錄和附加庫目錄的區別)
2)調試信息格式
- (沒有)
- 不創建調試信息
- 編譯時間更快
- / Z7
- 使用CodeView格式在.obj文件中生成完整的符號調試信息
- /Zi
- 使用程序數據庫格式在目標的.pdb文件中生成完整的符號調試信息。
- 支持最小重建(/ Gm),這可以減少重新編譯所需的時間。
- / ZI
- 除了支持Edit-and-Continue之外,生成像/ Zi這樣的調試信息
2.優化選項設置
在不同的配置中,release 和 debug,默認的優化選項也不一樣。
優化選項不一樣,最後生成的機器碼也不一樣。
根據需要可以進行自行配置
選擇啓用優化的話,針對一些沒有用的代碼,就會被優化掉,也就是目標文件中就不會有對應的機器碼了。這裏有介紹過應用。
3.彙編輸出文件設置
選擇彙編程序輸出,默認選擇的是 無列表【C/C++-》輸出文件-》彙編輸出程序】
其他值的含義:
/FA 僅輸出彙編到文件, 文件默認擴展名是 .asm。
/FAc 輸出彙編和相應的機器碼到文件,文件默認擴展名是 .cod。
/FAs 輸出彙編和相應的源代碼到文件,文件默認擴展名是 .asm。
/FAcs 輸出彙編、機器碼、源代碼到文件,文件默認擴展名是 .cod。
一般如要選的話,就選擇最後一個FAcs,可以通過這個輔助查找崩潰的具體行。具體可以查看https://blog.csdn.net/wind19/article/details/40614745
4.obj文件查看
可以使用dumpbin.exe 查看obj文件
dumpbin是在Windows平臺下用於顯示COFF格式文件信息的一個命令行工具。你可以使用DUMPBIN去顯示COFF格式的文件信息,比如像vc編譯器生成的目標文件(obj),可執行文件(exe)和動態鏈接庫(DLLs)等。
1.使用/summary選項,或者不輸入任何選項
顯示:每個段的基本信息(大小+段名)。
這裏.obj 是 COFF OBJECT
另外還有:
.lib 是 LIBRARY
.dll 是 DLL
.exe 是 EXECUTABLE IMAGE
2.使用/headers 選項
所有節(SECTION)的描述結構,即節頭
先顯示一個彙總,
再分別顯示各個section, SECTION HEADER #1 到 SECTION HEADER #B,11個
最後再顯示上面1中顯示的summary
3.使用/section:段名 查看具體的段的內容
/summary 中顯示出來的段名
bss段存放的是未初始化的全局變量或(全局和局部的)靜態變量。
data段存放的是初始化的全局變量或(全局和局部的)靜態變量。
顯示各個段的一些信息
:預留空間,cpp中分別定義了一個初始化的和未初始化的全局變量int,分別在data段和bss段,均佔用4個字節。
:讀寫權限,bss和data段都是可讀可寫的段
:字節對齊align,影響預留空間 size of raw data,如果int int char 就是9,如果是int char int 就是12。
關於const變量屬於只讀。
const全局變量存儲在只讀數據段,編譯期最初將其保存在符號表中。當運行時第一次使用時爲其分配內存,在程序結束時釋放。
const局部變量存儲在棧中,棧區也是運行時纔有的。當運行時第一次使用時爲其分配內存,代碼塊結束時釋放。
vs
只有存在於data段全局變量和靜態變量,在編譯期就分配空間。(bss段編譯器不分配空間,的file pointer to raw data 是 空,bss段只是有個佔位符,運行的時候系統自動都初始化爲0)
4.使用/symbols 查看符號表
使用/symbols 查看符號表,符號就是通常指定義出的函數,全局變量。
源文件的全局符號 (global symbol) 分成強 (strong) 和弱 (weak) 兩類傳給彙編器
彙編器則將強弱信息編碼並保存在目標文件的符號表中
編譯器認爲函數與初始化了的全局變量都是強符號,而未初始化的全局變量則成了弱符號
5.運行庫設置
設置在【配置屬性-》C/C++->代碼生成-》運行庫】
C和C++運行時庫 由編譯器分別實現,實現的內容是C標準和C++標準定義了一系列常用的函數(標準只是定義函數原型,編譯器來實現),實現的庫分別稱爲CRT庫和C++類庫。
比如 main() exit() 就是CRT 提供c程序運行的基本函數。
VC2010使用的CRT庫的DLL版本在MSVCR100.DLL中實現,對應調試版本爲MSVCR100D.DLL。
VC2010使用的CRT庫的LIB版本在libcmt.lib中實現,對應的調試版爲libcmtd.lib
VC2010使用的C++類庫的DLL版本在MSVCP100.DLL中實現,對應調試版本爲MSVCP100D.DLL。
VC2010使用的C++類庫的LIB版本在libcpmt.lib中實現,對應的調試版爲libcpmtd.lib
由於C++對C的兼容性,C++標準庫包括了C標準庫,除此之外還包括IO流和標準模板庫STL
(用Dependency Walker打開MSVCRT100.DLL,我們可以在其中找到我們經常使用使用的C函數,如printf ,getchar,malloc等。打開MSVCP100.DLL,也可以找到這些C函數。)
【配置屬性-》C/C++->代碼生成-》運行庫】中的不同選項就是選擇不同的DLL版本還是LIB版本,調試版本還是發佈版本:
MT選項:LIB版的C和C++運行庫。在鏈接時就會在將C和C++運行時庫集成到程序中成爲程序中的代碼,程序體積會變大。
MTd選項:LIB的調試版。
MD選項:DLL版的C和C++運行庫,這樣在程序運行時會動態的加載對應的DLL,程序體積會減小,缺點是程序在系統沒有對應DLL時程序無法運行。
MDd選項:DLL的調試版。
6.編譯錯誤
1) C1189 #error: /RTCc rejects conformant code, so it is not supported by the C++ Standard Library. Either remove this compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have received this warning.
代碼生成-》較小類型檢查 從/RTCc 改成 否
2)error C2039: “unique_ptr”: 不是“std”的成員
因爲沒有包含 unique_ptr 所在的頭文件 #include<memory>
3)c1xx : fatal error C1083: 無法打開源文件:“lllll”: No such file or directory
看看是不是真的無法打開 lllll 沒有找到這個源文件
4)無法打開預編譯頭文件:“xxx.pch”: No such file or directory
將 C++->預編譯頭 【創建/使用編譯頭】 改爲“不使用編譯頭”
5)命令行編譯msbuild 提示找不到頭文件
在jenkins上使用 msbuild命令行編譯工程的時候 提示找不到某頭文件,但打開vs工程編譯沒有問題。
原因是在工程的配置中有添加頭文件目錄,但是 msbuild 不認識$(SolutionDir)這個宏,所以提示找不到頭文件
解決加上一個參數,定義此宏:
/p:SolutionDir=path
解決方法二:使用硬鏈接,放到系統的include下
cd /d C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include
mklink /D 取個文件夾名 頭文件真正所在的路徑
6) msbuild和devenv的區別 還沒搞明白
在jenkins上編譯工程的時候,同一個sln下,有的用msbuild 有的用devenv。
set MSBuild=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe
set devenv="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"
set Src=%JENKINS_HOME%\jobs\Checkout\workspace\XXX\Src
set PubSrc=%JENKINS_HOME%\jobs\Checkout\workspace\YYY\Project
set Config=/t:Rebuild /p:Configuration=Release;Platform=Win32
set DevConfig=/Rebuild "Release|Win32"
cd /d %Src%\UnrealBuildTool\
::Msbuild
%MSBuild% UnrealBuildTool.csproj %Config%;OutputPath=..\..\Intermediate\UnrealBuildTool\Release\
%MSBuild% %PubSrc%\Common.vcxproj %Config%
::devenv
%devenv% %Src%\GamePhysLibDev.vcxproj %DevConfig%
鏈接:
鏈接器 (linker) 將一個個的目標文件 ( 還會有若干程序庫 ) 鏈接在一起生成一個完整的可執行文件。
1.一些鏈接錯誤
我們經常在遇到一些連接錯誤
1.LNK1104 ,這裏遇到過一次。
2.LNK2005 ,重定義錯誤,可能跟不同項目使用了不同的運行庫有關係(上述的運行庫設置)
3.LNK1169
在鏈接的時候有三個規則:
規則 1: 不允許強符號被多次定義 ( 即不同的目標文件中不能有同名的強符號 ) ;
規則 2: 如果一個符號在某個目標文件中是強符號,在其它文件中都是弱符號,那麼選擇強符號;
規則 3: 如果一個符號在所有目標文件中都是弱符號,那麼選擇其中任意一個;
2.附加依賴庫
或者 #pragma comment(lib, “**.lib”)
3.符號解析過程
在符號解析 (symbol resolution) 階段,鏈接器按照所有目標文件和庫文件出現在命令行中的順序(配置屬性-鏈接器-命令行)從左至右把他們放入輸入文件列表中,然後依次掃描它們,在此期間它要維護若干個集合 :
(1) 集合 E 是將被合併到一起組成可執行文件的所有目標文件集合;
(2) 集合 U 是未解析符號 (unresolvedsymbols ,比如已經被引用但是還未被定義的符號 ) 的集合;
(3) 集合 D 是所有之前已被加入到 E 的目標文件定義的符號集合。
一開始,這三個集合都是空的。 鏈接器的工作過程:
(1) 對命令行中的每一個輸入文件 f ,鏈接器確定它是目標文件還是庫文件,如果它是目標文件,就把 f 加入到 E ,並把 f 中未解析的符號和已定義的符號分別加入到 U 、 D 集合中,然後處理下一個輸入文件。
(2) 如果 f 是一個庫文件,鏈接器會嘗試把 U 中的所有未解析符號與 f 中各目標模塊定義的符號進行匹配。如果某個目標模塊 m 定義了一個 U 中的未解析符號,那麼就把 m 加入到 E 中,並把 m 中未解析的符號和已定義的符號分別加入到 U 、 D 集合中。不斷地對 f 中的所有目標模塊重複這個過程直至到達一個不動點 (fixed point) ,此時 U 和 D 不再變化。而那些未加入到 E 中的f 裏的目標模塊就被簡單地丟棄,鏈接器繼續處理下一輸入文件。
(3) 如果處理過程中往 D 加入一個已存在的符號,或者當掃描完所有輸入文件時 U 非空,鏈接器報錯並停止動作。否則,它把 E 中的所有目標文件合併在一起生成可執行文件。
可執行文件
編譯鏈接生成的可執行文件
1.可以在操作系統中直接運行
2.包含:1)機器碼和數據 2)相關的描述信息,比如程序有多大,要佔多少內存空間
操作系統根據描述信息,講可執行文件中的機器碼和數據裝入內存,並進行相關的初始化操作,比如將設置cs:ip指向第一條要執行的指令。
是誰將可執行文件中程序裝載進入內存並使它運行
DOS系統中是通過command.com 命令解釋器將可執行文件裝入內存中。
command設置cpu的cs:ip指向程序的第一條命令,將cpu的控制權交給他,然後程序退出以後,控制器交回給command
debug可以將程序加載入內存,但是不放棄對cpu的控制,這樣就可以利用debug的相關命令來單步執行程序。