欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Visual Studio調(diào)試C/C++教程指南

 更新時(shí)間:2024年06月11日 11:13:12   作者:-飛鶴-  
VisualStudio是微軟開(kāi)發(fā)的一款集成開(kāi)發(fā)環(huán)境軟件,本文主要介紹了Visual Studio調(diào)試C/C++教程指南,熟悉地掌握基于VS的C/C++調(diào)試技術(shù),可以大幅提升調(diào)試性能,感興趣的可以了解一下

1. 前言

Visual Studio(VS)是微軟開(kāi)發(fā)的一款集成開(kāi)發(fā)環(huán)境(IDE)軟件,支持C/C++、C#、VB、Python等開(kāi)發(fā)語(yǔ)言,開(kāi)發(fā)桌面、Web等應(yīng)用程序。VS功能極其強(qiáng)大,使用極其便利,用戶數(shù)量最多,被譽(yù)為"宇宙第一IDE"。

熟悉地掌握基于VS的C/C++調(diào)試技術(shù),可以大幅提升調(diào)試性能。隨著VS版本的更新,其功能越來(lái)越強(qiáng)大,本文的內(nèi)容是基于VS2019進(jìn)行驗(yàn)證測(cè)試的,之前版本VS可能有少量特性不支持。

Visual Studio下載

2. 基礎(chǔ)

2.1. 調(diào)試

代碼調(diào)試主要指使用調(diào)試工具來(lái)檢查和修復(fù)代碼中的錯(cuò)誤和問(wèn)題。代碼調(diào)試主要有運(yùn)行調(diào)試、打印調(diào)試、內(nèi)存分析、靜態(tài)分析、性能分析等。

2.2. 符號(hào)文件

符號(hào)文件(Symbol File)是指在編譯程序時(shí)生成的包含調(diào)試信息的文件。它們通常與可執(zhí)行文件或動(dòng)態(tài)鏈接庫(kù)(DLL)配對(duì)存在,用于提供程序的調(diào)試信息。VC生成的符號(hào)文件為PDB(Program Database)文件。其中存儲(chǔ)變量名、函數(shù)名、代碼行號(hào)、類型信息和棧信息等。exe/dll與pdb文件是一一對(duì)應(yīng)的。每次重新編譯代碼,都會(huì)生成新的pdb。

2.3. 調(diào)試器

Microsoft Visual C/C++的調(diào)試器名稱叫做"Visual Studio Debugger"。在調(diào)試exe時(shí),其會(huì)讀取exe文件中記錄的PDB路徑信息(這個(gè)路徑是開(kāi)發(fā)電腦編譯時(shí)生成的PDB路徑),如果這個(gè)PDB路徑不存在,那么調(diào)試器會(huì)在exe目錄去找PDB,如果依然找不到PDB,則啟用無(wú)PDB調(diào)試。無(wú)PDB調(diào)試只能查看匯編信息和寄存器信息。

調(diào)試方式

3. 本地調(diào)試

VS工程默認(rèn)即為本地調(diào)試(Local Windows Debugger)。選定啟動(dòng)工程,按F5或通過(guò)菜單Debug->Start Debugging。

  • 命令行參數(shù)(Command Arguments),給exe配置命令行參數(shù)。

  • 附加(Attach),默認(rèn)No。Yes表示附加當(dāng)前路徑的進(jìn)程進(jìn)行調(diào)試。

3.1. 遠(yuǎn)程調(diào)試

  • 將開(kāi)發(fā)電腦上的Remote Debugger目錄拷貝到生產(chǎn)電腦。

  • 根據(jù)程序的類型x64/x86打開(kāi)相應(yīng)的目錄,并打開(kāi)生產(chǎn)電腦目錄下的msvsmon.exe。

  • 首次調(diào)用時(shí),會(huì)彈出遠(yuǎn)程調(diào)試配置窗口,勾選所有的允許遠(yuǎn)程調(diào)試器與這些網(wǎng)絡(luò)通信。

  • 配置msvsmon.exe的Tools->Options。4015是默認(rèn)的端口號(hào),一般不建議修改。

  • 獲取生產(chǎn)電腦的IP,局域網(wǎng)網(wǎng)絡(luò)通信,可以使用計(jì)算機(jī)名:端口號(hào)的方式,也可以使用IP:端口號(hào)的方法。但是在訪問(wèn)跨網(wǎng)關(guān)的局域網(wǎng)電腦時(shí),計(jì)算機(jī)名可能無(wú)法解析出對(duì)應(yīng)的IP地址,導(dǎo)致訪問(wèn)失敗,所以更推薦IP:端口號(hào)的訪問(wèn)方式。
  • 依據(jù)下圖配置相關(guān)信息。

  • 按F5啟動(dòng)調(diào)試,調(diào)試遠(yuǎn)程exe和調(diào)試本地exe后續(xù)操作完全一致。

3.2.  附加調(diào)試

  • 打開(kāi)exe。

  • 從菜單啟動(dòng)Debug->Attach to Process,選擇需要調(diào)試的進(jìn)程進(jìn)行附加。如果是遠(yuǎn)程進(jìn)程,配置下圖信息。

3.3. 外網(wǎng)調(diào)試

遠(yuǎn)程調(diào)試一般是針對(duì)局域網(wǎng)進(jìn)行調(diào)試。但是有些時(shí)候,問(wèn)題進(jìn)程在外地,出差不方便或成本太高,非常需要一種能夠穿透廣域網(wǎng)進(jìn)行調(diào)試的方法。最簡(jiǎn)單的方法是使用VPN將目標(biāo)電腦遠(yuǎn)程連接到開(kāi)發(fā)電腦,這樣目標(biāo)電腦和開(kāi)發(fā)電腦就相當(dāng)于處在同一個(gè)局域網(wǎng),就可以使用普通的遠(yuǎn)程調(diào)試來(lái)進(jìn)行外網(wǎng)目標(biāo)電腦調(diào)試。

3.4. DLL調(diào)試

在DLL工程的屬性中Debugging的Command中選擇要執(zhí)行的exe,然后在dll中設(shè)置相關(guān)斷點(diǎn)。再按F5調(diào)試,即會(huì)中斷在DLL工程的斷點(diǎn)處。

4. 斷點(diǎn)調(diào)試

int 3是x86-64架構(gòu)CPU上的中斷指令,用于在程序執(zhí)行過(guò)程中觸發(fā)軟件中斷。VS在給代碼添加斷點(diǎn)時(shí),就是將指定行對(duì)應(yīng)的代碼修改為int 3指令,并且調(diào)試器接管代碼。繼續(xù)單步執(zhí)行時(shí),會(huì)還原int 3覆蓋的代碼。

4.1. 斷點(diǎn)類型

4.1.1. 普通斷點(diǎn)

在代碼指定行按F9或右鍵菜單Breakpoint->Insert Breakpoint設(shè)置普通斷點(diǎn)。

4.1.2. 條件斷點(diǎn)

在斷點(diǎn)上右鍵選擇Conditions。

設(shè)置 i == 5, 然后點(diǎn)擊Close。按F5執(zhí)行,代碼會(huì)停止在斷點(diǎn)處,此時(shí)i==5。

指定當(dāng)前斷點(diǎn)觸發(fā)指定次數(shù)時(shí)中斷下來(lái)。

當(dāng)前斷點(diǎn)運(yùn)行在指定線程時(shí)才中斷下來(lái)。

4.1.3. 行為斷點(diǎn)

行為斷點(diǎn)(Actions Breakpoint),也稱Tracepoint,因?yàn)閿帱c(diǎn)觸發(fā)時(shí)會(huì)在Output窗口打印信息。Continue Code Execution勾選表示不停止在斷點(diǎn)處,如果選空表示停止在斷點(diǎn)處。

Output窗口顯示:The value of z is 0x0000001e. 0x2C74

$PID,是偽指令??捎玫膫沃噶钊缦拢?/p>

4.1.4. 數(shù)據(jù)斷點(diǎn)

數(shù)據(jù)斷點(diǎn)(Data Breakpoint),當(dāng)然變量地址的內(nèi)容發(fā)生改變時(shí),即中斷下來(lái)。如下圖,Address編輯框可以直接填寫變量的地址,也可以使用取地址符來(lái)獲取變量的地址。數(shù)據(jù)斷點(diǎn)只能針對(duì)有效數(shù)據(jù)設(shè)置斷點(diǎn),并且只能在已經(jīng)開(kāi)始調(diào)試之后,在Breakpoint窗口的菜單New->Data Breakpoint來(lái)設(shè)置。

4.1.5. 系統(tǒng)函數(shù)斷點(diǎn)

例如想在CreateFile函數(shù)中下斷點(diǎn)。可以使用dhb.exe在相應(yīng)的

dbh.exe -s:srv*C:\Symbols*Symbol information -d C:\Windows\SysWOW64\kernel32.dll enum *CreateFile*

然后Breakpoints->New->Function Breakpoint:

運(yùn)行就會(huì)斷在系統(tǒng)API函數(shù)處,通過(guò)調(diào)用棧查找到調(diào)用的函數(shù)。

4.1.6. 軟件斷點(diǎn)

除了通過(guò)VS來(lái)添加斷點(diǎn)外,我們也可以在代碼中主動(dòng)添加軟件斷點(diǎn)__debugbreak()/DebugBreak函數(shù),或是斷言ASSERT(0)。

__debugbreak()/DebugBreak是代碼到此處立即中斷,而斷言則是根據(jù)參數(shù)邏輯值來(lái)決定是否中斷。軟件斷點(diǎn)主要用來(lái)在代碼潛在的異常出現(xiàn)時(shí)產(chǎn)生中斷提示開(kāi)發(fā)者。

4.2. 調(diào)試行為

4.2.1. 基本行為

工具欄或Debug菜單或鼠標(biāo)右鍵都有調(diào)試行為的選項(xiàng)。

  • Break All,中斷當(dāng)前所有正在執(zhí)行的代碼。當(dāng)代碼進(jìn)入死循環(huán)時(shí),點(diǎn)擊Break All,代碼即會(huì)中斷下來(lái),此時(shí)遍歷線程查看函數(shù)調(diào)用棧,即能發(fā)現(xiàn)死循環(huán)代碼位置。

  • Stop Debugging,停止當(dāng)前調(diào)試。

  • Restart,重新開(kāi)始調(diào)試。

  • Show Next Statement,光標(biāo)跳到下一次要執(zhí)行的代碼處。

  • Step Into,快捷鍵F11,進(jìn)入當(dāng)前語(yǔ)句所調(diào)用的函數(shù)內(nèi)部,并停在函數(shù)的第一行。如果當(dāng)前行沒(méi)有函數(shù)調(diào)用,則直接運(yùn)行至下一行。

  • Step Over,快捷鍵F10,執(zhí)行當(dāng)前語(yǔ)句,但不進(jìn)入函數(shù)內(nèi)部。

  • Step Out, 快捷鍵Shift+F11,執(zhí)行完當(dāng)前函數(shù),暫停在函數(shù)調(diào)用處。

  • Step Backward,即退回到上一次代碼暫停的位置,恢復(fù)上次調(diào)試的信息。Step Forward,前行到退回前的代碼暫停的位置。這是基于棧快照進(jìn)行的調(diào)試,VS會(huì)在幾次中斷時(shí),保存對(duì)應(yīng)棧信息,尤其是當(dāng)我們想反復(fù)調(diào)試一個(gè)算法時(shí),使用Step Backward會(huì)非常方便。

4.2.2. 高級(jí)行為

  • Run To Cursor,運(yùn)行到光標(biāo)處中斷,可以通過(guò)鼠標(biāo)右鍵來(lái)執(zhí)行,也可在光標(biāo)處的代碼行的圖標(biāo)上點(diǎn)擊。

  • Force Run To Cursor,強(qiáng)制運(yùn)行到光標(biāo)處,會(huì)跳過(guò)中間設(shè)置的斷點(diǎn)。

  • 自定義,鼠標(biāo)放在箭頭上,可以將下一個(gè)可執(zhí)行代碼的位置拖動(dòng)到任意位置。拖動(dòng)需要依賴先前的代碼,否則可能產(chǎn)生異常。

5. 調(diào)試窗口

5.1. Output 

Output Debug窗口主要輸出調(diào)試過(guò)程的信息,主要包括:

  • Exception Messages

  • Step Filtering Messages

  • Module Load Messages

  • Module Unload Messages

  • Process Exit Messages

  • Thread Exit Messages

  • Program Qutput

其中Program Output是代碼運(yùn)行時(shí)輸出的信息,主要通過(guò)Trace函數(shù)或OutputDebugString函數(shù)來(lái)將信息輸出到Output窗口。如果是非調(diào)試狀態(tài)下,OutputDebugString的輸出信息則需要DbgView工具來(lái)接收并顯示。

5.2. Locals

Locals窗口顯示調(diào)試時(shí)當(dāng)前執(zhí)行代碼所在函數(shù)所在的棧的局部變量的值和類型。

5.3. Autos

Autos窗口顯示調(diào)試時(shí)當(dāng)前執(zhí)行代碼所在函數(shù)所在的棧的上下文棧變量值和類型。

5.4. Watch

Watch窗口總共有4個(gè),可以在菜單Debug->Windows->Wartch中選擇。Watch窗口可以顯示當(dāng)前棧內(nèi)存的局部變量,全局變量等,支持16進(jìn)制形式顯示,并支持實(shí)時(shí)修改變量值。

支持簡(jiǎn)單的表達(dá)式顯示,如x+y, sizeof(x)等。

支持格式化顯示,如(int*)(szBuff),4將Buff轉(zhuǎn)換為int*,再格式化為4個(gè)元素顯示。

Watch窗口還支持顯示偽變量,如:

  • $tid – 當(dāng)前線程的的線程 ID

  • $pid – 進(jìn)程 ID

  • $cmdline – 附加進(jìn)程的命令行字符串

  • $user – 運(yùn)行在程序中的賬戶信息

  • $registername – 顯示指定寄存器的寄存器內(nèi)容

  • $err – 顯示最近錯(cuò)誤的錯(cuò)誤碼

  • $err, hr – 顯示最近錯(cuò)誤的消息

5.5. Memory

Memory窗口是用來(lái)顯示地址對(duì)應(yīng)的內(nèi)容,Memory窗口有4個(gè)從菜單Debug->Windows->Memory中選擇。

Adress編輯框填寫變量地址,Columns選擇每行顯示的內(nèi)容數(shù)量。

5.6. QuickWatch

QuickWatch窗口是快捷觀察、修改變量的窗口。

5.7. Disassembly

參數(shù)傳遞、函數(shù)返回等一些復(fù)雜的語(yǔ)法形式,要想理解其深層執(zhí)行邏輯,就需要單步執(zhí)行匯編代碼來(lái)觀察其隱藏邏輯細(xì)節(jié)。

5.8. Registers

寄存器窗口,顯示當(dāng)線程的寄存器信息。

5.9. Call Stack

函數(shù)調(diào)用棧窗口,顯示當(dāng)前線程的函數(shù)棧調(diào)用情況??梢酝ㄟ^(guò)Threads窗口選定當(dāng)前線程。

5.10. Immediate Window

Debug->Windows->Immediate窗口。立即窗口主要用來(lái)查看、修改變量,執(zhí)行函數(shù),表達(dá)式等。

6. 多線程

6.1. Threads

Threads是顯示當(dāng)前進(jìn)程的所有運(yùn)行的線程信息的窗口。雙擊線程所在行,即將當(dāng)前代碼窗口、調(diào)用棧窗口等相關(guān)窗口的內(nèi)容更新為選定線程。

當(dāng)進(jìn)程有多個(gè)工作者線程時(shí),且只想調(diào)試其中一個(gè)線程時(shí),可以將不關(guān)心的線程使用Freeze冰凍起來(lái)。冰凍的線程在按F5運(yùn)行時(shí),依然冰凍著不運(yùn)行。使用Thaw解凍線程,線程將恢復(fù)為正??梢哉{(diào)試的狀態(tài)。

6.2. 條件斷點(diǎn)

在多線程中,根據(jù)線程信息來(lái)設(shè)置相應(yīng)的斷點(diǎn)來(lái)觀察期望的變量信息。

6.3. Parallel Watch

Debug->Window->Parallel Watch可以顯示幾個(gè)線程同時(shí)調(diào)用的函數(shù)變量的情況,更方便地調(diào)試多線程。

6.4. 線程結(jié)構(gòu)圖

打開(kāi)Debug->Windows->Parallel Stack窗口,會(huì)顯示所有線程的棧,并顯示當(dāng)前線程棧。相比Threads窗口更直觀。

7. 參數(shù)配置

7.1. 增量鏈接

增量鏈接在Debug下默認(rèn)打開(kāi),在Release下默認(rèn)關(guān)閉。通過(guò)Linker->General->Enable Incremental Linking來(lái)開(kāi)啟和關(guān)閉。增量鏈接還需要配置C/C++->General->Debug Information Format->Program Database for Edit And Continue (/ZI)。

增量鏈接的用處是在斷點(diǎn)單步調(diào)試代碼的時(shí)候,編輯代碼,然后繼續(xù)單步執(zhí)行時(shí),VS會(huì)自動(dòng)增量鏈接,不用重新編譯源代碼,然后繼續(xù)單步執(zhí)行代碼進(jìn)行調(diào)試。

增量鏈接是調(diào)試時(shí)使用,增量編譯則是編譯時(shí)只編譯修改的源文件,兩者不相同。

7.2. 優(yōu)化級(jí)別

Debug版本下,默認(rèn)關(guān)閉代碼優(yōu)化,此時(shí)代碼的調(diào)試信息與代碼源文件是一一對(duì)應(yīng),調(diào)試更方便。Release版本下,默認(rèn)使用O2優(yōu)化,此時(shí)代碼優(yōu)化程度非常大,調(diào)試信息與代碼源文件無(wú)法一一對(duì)應(yīng)。如果想調(diào)試Release版本,則需要關(guān)閉優(yōu)化。

7.3 宏展開(kāi)

一些復(fù)雜的宏,非常難以調(diào)試。如何查看宏預(yù)處理之后展開(kāi)形成的代碼呢?C/C++->Preporcessor->Preprocess to a File配置默認(rèn)是關(guān)閉的,打開(kāi)此配置為Yes(/P)表示將生成預(yù)處理后的文件,文件與源文件同名,后綴為.i。在.i文件中可以查看所有宏展開(kāi)的結(jié)果,以及其他預(yù)處理的結(jié)果。

7.4. 顯示鏈接細(xì)節(jié)

VS 鏈接器默認(rèn)只顯示一些關(guān)鍵的鏈接信息??梢酝ㄟ^(guò)Linker->General->Show Progress配置Display all progress messages(/VERBOSE)來(lái)顯示詳細(xì)的鏈接信息,可以更方便地分析一些鏈接異常的錯(cuò)誤。

7.5. 警告

7.5.1. 編譯警告

為了提升代碼的可靠性,警告也需要認(rèn)真對(duì)等。

  • C/C++->All Options->Warning Level,選擇合適的警告級(jí)別,初期可以選擇W3級(jí)別。

  • C/C++->All Options->Treat Warning As Errors,初期可以不設(shè)置。

  • C/C++->All Options->Treat Specific Warnings As Errors,可以將一些關(guān)鍵警告設(shè)置為錯(cuò)誤。

7.5.2. 鏈接警告

Linker->All Options->Treat Linker Warning As Errors,根據(jù)需要將警告作為錯(cuò)誤對(duì)待。

Dump調(diào)試

8.概念

Dump文件(Dump File),也叫轉(zhuǎn)儲(chǔ)文件,以.DMP為文件后綴。dump文件是進(jìn)程在內(nèi)存中的鏡像文件,通過(guò)轉(zhuǎn)換然后存儲(chǔ)成以.DMP后綴的文件。dump文件根據(jù)存儲(chǔ)時(shí)的選項(xiàng)不同,會(huì)生成不同大小的文件,其中記錄信息也自然有所不同。

8.1. 轉(zhuǎn)儲(chǔ)文件生成

  • 通過(guò)Windows進(jìn)程管理器,選擇指定的進(jìn)程,右鍵生成轉(zhuǎn)儲(chǔ)文件。

  • 通過(guò)函數(shù)MiniDumpWriteDump生成轉(zhuǎn)儲(chǔ)文件。

8.2. 調(diào)試轉(zhuǎn)儲(chǔ)文件

  • 選擇與生成Dump文件相同版本的VS。

  • 啟動(dòng)VS并打開(kāi)Dump文件。

  • 必須保證生成Dump文件的程序的PDB文件和源代碼相一致。

  • VS2005打開(kāi)Dump文件時(shí),直接按F5調(diào)試,代碼會(huì)停在出錯(cuò)的地方,通過(guò)Call Stack窗口查看。

  • VS2010打開(kāi)Dump文件時(shí),

需要通過(guò)Set symbol paths設(shè)置符號(hào)文件路徑,也即PDB文件路徑。然后點(diǎn)擊Debug with Native Only,代碼即會(huì)中斷在出錯(cuò)的地方,通過(guò)Call Stack窗口查看相關(guān)信息。

變量溢出

變量?jī)?nèi)存溢出分為上溢(Overflow)和下溢(Underflow)。內(nèi)存溢出導(dǎo)致的異常是C++代碼最為難以調(diào)試的Bug之一,因?yàn)閮?nèi)存溢出導(dǎo)致的異常往往不會(huì)立即出現(xiàn),而是展現(xiàn)在后面的函數(shù)中。因?yàn)閮?nèi)存溢出可能覆蓋了后面的代碼,導(dǎo)致后面的代碼執(zhí)行異常。

AddressSanitizer (ASan) 是一種編譯器和運(yùn)行時(shí)技術(shù),它通過(guò)和編譯器配合,通過(guò)插樁技術(shù),向內(nèi)存的前后添加標(biāo)識(shí),來(lái)識(shí)別運(yùn)行時(shí)產(chǎn)生的上溢和下溢異常。其對(duì)代碼執(zhí)行性能影響較大,VS中默認(rèn)是關(guān)閉的,需要手動(dòng)打開(kāi)。

ASan可以識(shí)別棧內(nèi)存、堆內(nèi)存、靜態(tài)內(nèi)存的溢出,另外也能檢測(cè)重復(fù)釋放,釋放后使用內(nèi)存。

通過(guò)C/C++->General->Enable Address Sanitizer使能,此配置與編輯并繼續(xù)、增量鏈接和/RTC不兼容。

更多信息:AddressSanitizer | Microsoft Learn

資源泄露

電腦上的內(nèi)核資源是有限的,申請(qǐng)使用完了,就要釋放,否則資源可能不夠用,導(dǎo)致后續(xù)申請(qǐng)失敗而運(yùn)行異常。所以資源泄露也是比較嚴(yán)重的問(wèn)題,需要解決。

10. 內(nèi)存泄露

在調(diào)試運(yùn)行進(jìn)程時(shí),退出調(diào)試后,可能會(huì)在VS的Output窗口中顯示如下信息:

這就是內(nèi)存泄露的提示信息。上面的信息有時(shí)會(huì)指出內(nèi)存泄露的位置,有的時(shí)候指出的位置卻不準(zhǔn)確。第三方工具 Visual Leak Detector,可以用來(lái)檢測(cè)內(nèi)存泄露,其主要是通過(guò)重載內(nèi)存申請(qǐng)和釋放函數(shù),然后記錄內(nèi)存申請(qǐng)的地址和詳細(xì)源文件行號(hào),最后退出時(shí)檢測(cè)所有申請(qǐng)的地址是否釋放,如果沒(méi)有釋放,則打印出內(nèi)存申請(qǐng)的信息。

Visual Leak Detector的使用非常簡(jiǎn)單,下載安裝,然后在工程的啟動(dòng)接口文件中添加#include <vld.h>即可。

詳細(xì)信息見(jiàn):Home · KindDragon/vld Wiki · GitHub

10.1. 句柄泄露

除了內(nèi)存泄露外,句柄也是容易泄露的資源。

  • 可以使用WinDbg的句柄快照對(duì)比功能找出未正常釋放的句柄。

  • 可以使用第三方工具Deleaker,可以注冊(cè)試用版本使用14天。Deleaker會(huì)檢測(cè)出代碼申請(qǐng)未釋放的行號(hào)。

11. 靜態(tài)調(diào)試

相比于動(dòng)態(tài)調(diào)試(在代碼運(yùn)行時(shí)調(diào)試),靜態(tài)調(diào)試是在代碼編譯時(shí)來(lái)調(diào)試,其效率更高。

11.1. 靜態(tài)斷言

靜態(tài)斷言static_assert是C++11中引入的新語(yǔ)法,可以在編譯時(shí)進(jìn)行一些判斷,并打印相應(yīng)信息。

static_assert(sizeof(int) == 4, "int must be 4 bytes"); // 檢查 int 類型是否為 4 字節(jié)

template <typename T>
void process(T value) {
    static_assert(std::is_integral<T>::value, "T must be an integral type"); // 檢查模板類型是否為整數(shù)類型
}

11.2. 靜態(tài)分析

VS自帶的靜態(tài)分析,功能非常強(qiáng)大,能夠發(fā)現(xiàn)很多隱藏的問(wèn)題。開(kāi)啟靜態(tài)分析之后,會(huì)在編譯期間進(jìn)行靜態(tài)分析代碼,所以會(huì)加大編譯時(shí)間。建議定期開(kāi)啟靜態(tài)分析檢查代碼,并修復(fù)相關(guān)問(wèn)題。

另外還可以選擇相應(yīng)的靜態(tài)分析規(guī)則:

12. 性能調(diào)試

VS提供了性能度量工具(Performance Profiler),幫助開(kāi)發(fā)者優(yōu)化代碼和提高應(yīng)用程序性能。Profiler的主要功能是診斷內(nèi)存、CPU 使用率。Debug下的性能分析,因?yàn)橛泻芏嗾{(diào)試及優(yōu)化的影響,所以很不準(zhǔn)確,建議是在Release版本下進(jìn)行性能調(diào)試分析。

通過(guò)Debug->Performance Profiler。C/C++更多用來(lái)分析CPU和Memory。

點(diǎn)擊Start之后,目標(biāo)程序開(kāi)始執(zhí)行,并開(kāi)始監(jiān)控性能,并通過(guò)Take Snapshot給當(dāng)前進(jìn)程拍攝快照。

12.1. 內(nèi)存分析

通過(guò)Stop Collection或等進(jìn)程結(jié)束,會(huì)顯示如下信息。每個(gè)快照會(huì)保存詳細(xì)的進(jìn)程堆棧信息。

點(diǎn)擊上面的內(nèi)存分配次數(shù)、分配次數(shù)增量等數(shù)據(jù),可以獲取詳細(xì)信息:

雙擊相應(yīng)對(duì)象分配行,可以得到詳細(xì)的對(duì)象信息包括調(diào)用堆棧信息,通過(guò)調(diào)用??梢圆榭聪鄳?yīng)的代碼:

12.2. CPU分析

內(nèi)存分類統(tǒng)計(jì):

內(nèi)存分線程統(tǒng)計(jì):

雙擊函數(shù)名,可以詳細(xì)函數(shù)占比分析:

12.3. 性能提示

調(diào)試器在斷點(diǎn)或單步執(zhí)行操作中停止執(zhí)行時(shí),中斷與上一個(gè)斷點(diǎn)之間經(jīng)過(guò)的時(shí)間會(huì)顯示為在編輯器窗口中的提示。

更多信息見(jiàn):在 Visual Studio 中度量性能 - Visual Studio (Windows) | Microsoft Learn

協(xié)同調(diào)試

VS提供了多人協(xié)作進(jìn)行調(diào)試的功能,使用非常簡(jiǎn)單。

  • 登錄VS帳號(hào)。

  • 點(diǎn)擊如下圖中紅圈的圖標(biāo),啟用協(xié)作會(huì)話。

協(xié)作會(huì)話啟動(dòng)完成后,會(huì)顯示如下信息。默認(rèn)已經(jīng)將協(xié)作邀請(qǐng)鏈接進(jìn)行了拷貝。如果協(xié)作方只需要查看代碼,不需要調(diào)試,可以點(diǎn)擊Make read-only。

將協(xié)作邀請(qǐng)鏈接發(fā)給協(xié)作方并打開(kāi),彈出如下窗口。

打開(kāi)VS Code,主持人開(kāi)始調(diào)試,協(xié)作方也可以通過(guò)VS Code查看調(diào)試信息。

通過(guò)VS的File->Join Live Share Session來(lái)加入?yún)f(xié)作調(diào)試。

還可以進(jìn)行協(xié)作的管理,以及協(xié)作時(shí)聊天。

14. 調(diào)試技巧

14.1. 小技巧

  • 使用快捷鍵,另外還可以自定義快捷鍵。

  • 固定數(shù)據(jù)提示信息

  • 格式化內(nèi)存

調(diào)試器也能在 Watch 窗口中顯示格式化的內(nèi)存值,高達(dá) 64 個(gè)字節(jié)。你能用下面的說(shuō)明符在表達(dá)式(變量或內(nèi)存地址)后來(lái)格式化數(shù)據(jù)。

  • mb / m - BYTE

  • mw - WORD

  • md - DWORD

  • mq – 8BYTE

  • ma – 16BYTE

14.2. 管理員權(quán)限調(diào)試

通過(guò)Linker->Manifest File->UAC Execution Level->requireAdministrator,啟用管理員權(quán)限,然后啟用調(diào)試,則被調(diào)試的進(jìn)程也將獲取管理員權(quán)限。

14.3. 生成調(diào)用棧信息

void printStackTrace() 
{
    HANDLE process = GetCurrentProcess();
    SymInitialize(process, nullptr, TRUE);

    void* stack[100];
    WORD frames = CaptureStackBackTrace(0, 100, stack, nullptr);

    SYMBOL_INFO* symbol = (SYMBOL_INFO*)malloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char));
    symbol->MaxNameLen = 255;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

    for (WORD i = 0; i < frames; ++i) {
        SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);

        printf("[%d] %s\n", i, symbol->Name);
    }

    free(symbol);
}

14.4. 內(nèi)存耗盡

Windows下的32位應(yīng)用程序,只能申請(qǐng)2GB內(nèi)存。所以在申請(qǐng)過(guò)的總內(nèi)存過(guò)大時(shí),可能超過(guò)2GB,導(dǎo)致程序異常??梢允褂孟旅娲a提示總內(nèi)存過(guò)大的提示。

void NoMoreMemory()
{
    LPVOID lpMsgBuf;
    if (!FormatMessage( 
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    0x00000008, // Memory error	
     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL ))
    {
    return;
    }
    
    // Display the string.
    MessageBox( NULL, (LPCTSTR)lpMsgBuf, _T("Error"), MB_OK | MB_ICONINFORMATION );
    abort();
}

// Add to this function to the entry of EXE/DLL.
set_new_handler(NoMoreMemory);

14.5. 未初始化異常

14.5.1. Debug

VS在Debug下為了方便用戶調(diào)試,編譯器會(huì)強(qiáng)制將未初始化的變量強(qiáng)制賦值指定值做標(biāo)記。

棧變量強(qiáng)制賦值0xCCCCCCCC,堆內(nèi)存強(qiáng)制賦值為0xCDCDCDCD。

14.5.2. Release

在VS下,C/C++中的變量編譯器不會(huì)對(duì)變量進(jìn)行初始化。棧變量和堆內(nèi)存都是隨機(jī)的。養(yǎng)成變量初始化的習(xí)慣是提升代碼質(zhì)量的好習(xí)慣。

14.6. 調(diào)試運(yùn)行時(shí)庫(kù)代碼

VS運(yùn)行時(shí)庫(kù),有一些提供了代碼,有一些沒(méi)有提供。如CString的GetLength()函數(shù)。VS2012及之前的版本默認(rèn)不會(huì)進(jìn)入庫(kù)函數(shù),VS2015及之后版本默認(rèn)在使用Step Into時(shí)會(huì)進(jìn)入庫(kù)函數(shù)。如果無(wú)法進(jìn)入庫(kù)函數(shù),可以進(jìn)入?yún)R編調(diào)試,然后再Step Into就可以進(jìn)入庫(kù)函數(shù)了。

14.7. Debug和Release的差異

編譯優(yōu)化:

  • Debug 模式下,編譯器不會(huì)進(jìn)行任何優(yōu)化,以便于調(diào)試和錯(cuò)誤定位。

  • Release 模式下,編譯器會(huì)進(jìn)行各種優(yōu)化,以提高程序的性能和執(zhí)行效率。

調(diào)試信息:

  • Debug 模式下,編譯器會(huì)生成完整的調(diào)試信息,包括變量名、行號(hào)等,方便進(jìn)行調(diào)試。

  • Release 模式下,編譯器會(huì)盡量減少調(diào)試信息的生成,以減小程序的體積。

運(yùn)行時(shí)檢查:

  • Debug 模式下,編譯器會(huì)添加額外的運(yùn)行時(shí)檢查,如數(shù)組越界檢查、內(nèi)存泄漏檢查等,以幫助發(fā)現(xiàn)程序中的錯(cuò)誤。

  • Release 模式下,這些運(yùn)行時(shí)檢查通常會(huì)被禁用,以提高程序的執(zhí)行速度。

斷言和異常處理:

  • Debug 模式下,程序會(huì)更加嚴(yán)格地檢查各種斷言和異常,以幫助開(kāi)發(fā)者發(fā)現(xiàn)問(wèn)題。

  • Release 模式下,這些檢查通常會(huì)被禁用或簡(jiǎn)化,以提高程序的穩(wěn)定性和性能。

編譯器定義宏:

  • Debug 模式下,編譯器會(huì)定義 _DEBUG 宏,用于在代碼中進(jìn)行條件編譯。

  • Release 模式下,編譯器會(huì)定義 NDEBUG 宏,用于在代碼中進(jìn)行條件編譯。

編譯器優(yōu)化標(biāo)志:

  • Debug 模式下,編譯器通常會(huì)使用 /Od 標(biāo)志禁用優(yōu)化。

  • Release 模式下,編譯器通常會(huì)使用 /O2 標(biāo)志啟用最高級(jí)別的優(yōu)化。

鏈接器優(yōu)化:

  • Debug 模式下,鏈接器通常不會(huì)進(jìn)行任何優(yōu)化。

  • Release 模式下,鏈接器會(huì)進(jìn)行各種優(yōu)化,如去除未使用的函數(shù)和數(shù)據(jù)等。

運(yùn)行時(shí)庫(kù):

  • Debug 模式下,程序會(huì)鏈接到 Debug 版本的運(yùn)行時(shí)庫(kù),提供更多的錯(cuò)誤檢查和調(diào)試支持。

  • Release 模式下,程序會(huì)鏈接到 Release 版本的運(yùn)行時(shí)庫(kù),以提高性能和減小程序體積。

14.8. 構(gòu)建事件

有時(shí)需要在編譯前后附加一些操作來(lái)簡(jiǎn)單調(diào)試,就可以使用編譯事件配置,選擇相應(yīng)的構(gòu)建事件然后配置命令行來(lái)執(zhí)行需要附加的操作。

14.9. 日志

日志是調(diào)試工具的重要手段,除了使用Trace和OutputDebugString之外,還可以使用自定義的日志函數(shù)。

14.10. 調(diào)試窗口

VS自帶的Spy++可以用來(lái)查看窗口信息,還可以用來(lái)監(jiān)控窗口消息。

14.11. 查看DLL接口

dumpbin是VS自帶的命令行工具,可以用來(lái)查看DLL的接口函數(shù)。通過(guò)Tools->Command Line來(lái)使用。

這是一個(gè)命令行工具,可以用于查看 DLL 文件的結(jié)構(gòu)和內(nèi)容。常用命令:

  • dumpbin /exports <DLL_FILE_PATH>: 查看 DLL 的導(dǎo)出函數(shù)

  • dumpbin /dependents <DLL_FILE_PATH>: 查看 DLL 的依賴項(xiàng)

  • dumpbin /headers <DLL_FILE_PATH>: 查看 DLL 的頭部信息

15. 總結(jié)

調(diào)試是手段,不是目的,不能為了調(diào)試而調(diào)試。目的是更高效地開(kāi)發(fā),為此,減少問(wèn)題,減少調(diào)試才是最重要的。

到此這篇關(guān)于Visual Studio調(diào)試C/C++教程指南的文章就介紹到這了,更多相關(guān)Visual Studio調(diào)試C++內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vscode配置C++環(huán)境的圖文教程

    vscode配置C++環(huán)境的圖文教程

    這篇文章主要介紹了vscode配置C++環(huán)境的教程圖文詳解,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • C++字符串反轉(zhuǎn)的幾種方法

    C++字符串反轉(zhuǎn)的幾種方法

    通過(guò)不同的方法,實(shí)現(xiàn)對(duì)所輸入字符串的反轉(zhuǎn),具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-06-06
  • C語(yǔ)言實(shí)現(xiàn)飛機(jī)售票系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)飛機(jī)售票系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)飛機(jī)售票系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C語(yǔ)言直接插入排序算法介紹及示例

    C語(yǔ)言直接插入排序算法介紹及示例

    插入排序是把一個(gè)記錄插入到已排序的有序序列中,使整個(gè)序列在插入該記錄后仍然有序。插入排序中較簡(jiǎn)單的種方法是直接插入排序,其插入位置的確定方法是將待插入的記錄與有序區(qū)中的各記錄自右向左依次比較其關(guān)鍵字值的大小
    2022-08-08
  • C++實(shí)現(xiàn)LeetCode(35.搜索插入位置)

    C++實(shí)現(xiàn)LeetCode(35.搜索插入位置)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(35.搜索插入位置),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • c語(yǔ)言的程序環(huán)境與預(yù)處理詳解

    c語(yǔ)言的程序環(huán)境與預(yù)處理詳解

    大家好,本篇文章主要講的是c語(yǔ)言的程序環(huán)境與預(yù)處理詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • c++ KMP字符串匹配算法

    c++ KMP字符串匹配算法

    大家好,本篇文章主要講的是c++ KMP字符串匹配算法,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • C++圖像處理之雙邊濾波

    C++圖像處理之雙邊濾波

    這篇文章主要為大家詳細(xì)介紹了C++圖像處理之雙邊濾波,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • c語(yǔ)言描述回文數(shù)的三種算法

    c語(yǔ)言描述回文數(shù)的三種算法

    這篇文章主要介紹了c語(yǔ)言描述回文數(shù)的三種算法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • c++ 對(duì)數(shù)器實(shí)現(xiàn)示例

    c++ 對(duì)數(shù)器實(shí)現(xiàn)示例

    對(duì)數(shù)器用于在自己的本地平臺(tái)驗(yàn)證算法正確性,本文詳細(xì)的介紹了c++ 對(duì)數(shù)器實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08

最新評(píng)論