VS C++工程的編譯和鏈接

cl.exe和link.exe分別是visual studio 中的編譯器和鏈接器

配置屬性中的【c/c++】(設置編譯的一些選項) 和 【鏈接器】選項頁中,最後的那個命令行彙總了所有生效的設置,就是最終執行的命令行

配置屬性中的VC++ 目錄用於設置各個路徑,相當於設置環境變量PATH(搜可執行文件的路徑):

INCLUDE(搜include中文件的路徑)

LIBPATH(搜使用using 引入文件的路徑)

LIB(搜庫文件的路徑)

 

編譯過程:語法錯誤

鏈接過程:結構錯誤

運行過程:邏輯錯誤

目錄

編譯:

1.頭文件路徑設置

2.優化選項設置

3.彙編輸出文件設置

4.obj文件查看

1.使用/summary選項,或者不輸入任何選項

2.使用/headers 選項

 3.使用/section:段名 查看具體的段的內容

4.使用/symbols 查看符號表

5.運行庫設置

鏈接:

1.一些鏈接錯誤

2.附加依賴庫

3.符號解析過程

可執行文件


 

編譯:

一般一個大的項目,修改完一個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

https://stackoverflow.com/questions/15053915/how-to-get-rid-of-solutiondir-when-building-visual-studio-project-from-outs

解決方法二:使用硬鏈接,放到系統的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的相關命令來單步執行程序。

 

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