🚀 在 VS Code 中

在 Visual Studio Code 中使用 Clang

在本教學課程中,您將在 macOS 上設定 Visual Studio Code,以使用 Clang/LLVM 編譯器和偵錯工具。

設定 VS Code 後,您將在 VS Code 中編譯和偵錯 C++ 程式。本教學課程不會教您 Clang 或 C++ 語言。關於這些主題,網路上有許多優良資源可供參考。

如果您有任何問題,歡迎在 VS Code 文件存放庫中針對本教學課程提交問題。

先決條件

若要成功完成本教學課程,您必須執行下列步驟

  1. 在 macOS 上安裝 Visual Studio Code

  2. 安裝 VS Code 的 C++ 擴充功能。您可以透過在擴充功能檢視 (⇧⌘X (Windows、Linux Ctrl+Shift+X)) 中搜尋 'C++' 來安裝 C/C++ 擴充功能。

    C/C++ extension

確認已安裝 Clang

您的 Mac 可能已安裝 Clang。若要確認是否已安裝,請開啟 macOS 終端機視窗並輸入下列命令

clang --version

如果未安裝 Clang,請輸入下列命令以安裝命令列開發人員工具,其中包含 Clang

xcode-select --install

建立 Hello World 應用程式

從 macOS 終端機中,建立一個名為 projects 的空白資料夾,您可以在其中儲存所有 VS Code 專案,然後建立一個名為 helloworld 的子資料夾,導覽至該資料夾,並在該資料夾中開啟 VS Code,方法是在終端機視窗中輸入下列命令

mkdir projects
cd projects
mkdir helloworld
cd helloworld
code .

code . 命令會在目前的工作資料夾中開啟 VS Code,該資料夾會變成您的「工作區」。當您逐步完成本教學課程時,會在工作區的 .vscode 資料夾中建立三個檔案

  • tasks.json (編譯器建置設定)
  • launch.json (偵錯工具設定)
  • c_cpp_properties.json (編譯器路徑和 IntelliSense 設定)

新增 Hello World 原始碼檔案

在檔案總管標題列中,選取 [新增檔案] 按鈕,並將檔案命名為 helloworld.cpp

New File title bar button

貼上下列原始碼

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"};

    for (const string& word : msg)
    {
        cout << word << " ";
    }
    cout << endl;
}

現在按下 ⌘S (Windows、Linux Ctrl+S) 以儲存檔案。請注意,您的檔案會列在 VS Code 側邊欄的 [檔案總管] 檢視 (⇧⌘E (Windows、Linux Ctrl+Shift+E)) 中

File Explorer

您也可以啟用自動儲存以自動儲存您的檔案變更,方法是選取 [檔案] > [自動儲存]。您可以在 VS Code 使用者介面文件中找到關於其他檢視的詳細資訊。

注意:當您儲存或開啟 C++ 檔案時,您可能會看到來自 C/C++ 擴充功能的通知,告知您有 Insiders 版本可用,可讓您測試新功能和修正程式。您可以選取 X ([清除通知]) 來忽略此通知。

探索 IntelliSense

IntelliSense 是一種工具,可透過新增程式碼編輯功能 (例如程式碼完成、參數資訊、快速資訊和成員清單) 來協助您更快且更有效率地撰寫程式碼。

若要查看 IntelliSense 的實際運作,請將滑鼠指標停留在 vectorstring 上,以查看其類型資訊。如果您在第 10 行輸入 msg.,您可以看到建議呼叫的成員函式完成清單,全部由 IntelliSense 產生

Statement completion IntelliSense

您可以按下 Tab 鍵來插入選取的成員。然後,當您新增左括號時,會顯示函式所需引數的相關資訊。

如果尚未設定 IntelliSense,請開啟命令面板 (⇧⌘P (Windows、Linux Ctrl+Shift+P)) 並輸入 選取 IntelliSense 設定。從編譯器的下拉式清單中,選取 使用 clang++ 進行設定。您可以在 IntelliSense 設定文件中找到更多資訊。

執行 helloworld.cpp

請記住,C++ 擴充功能會使用您在電腦上安裝的 C++ 編譯器來建置您的程式。請確定您已安裝 C++ 編譯器 (例如 Clang),再嘗試在 VS Code 中執行和偵錯 helloworld.cpp

  1. 開啟 helloworld.cpp,使其成為作用中檔案。

  2. 按下編輯器右上角的播放按鈕。

    Screenshot of helloworld.cpp and play button

  3. 從系統上偵測到的編譯器清單中,選擇 [C/C++: clang++ 建置並偵錯作用中檔案]。

    Build and debug task

您只需在第一次執行 helloworld.cpp 時選擇編譯器。此編譯器是在 tasks.json 檔案中設定的「預設」編譯器。

  1. 建置成功後,您的程式輸出會出現在整合式 [偵錯主控台] 中。

    screenshot of program output

恭喜!您剛剛在 VS Code 中執行了您的第一個 C++ 程式!

了解 tasks.json

第一次執行程式時,C++ 擴充功能會建立 tasks.json,位於專案的 .vscode 資料夾中。tasks.json 儲存建置組態。

以下是 macOS 上 tasks.json 檔案的範例

{
  "tasks": [
    {
      "type": "cppbuild",
      "label": "C/C++: clang++ build active file",
      "command": "/usr/bin/clang++",
      "args": [
        "-fcolor-diagnostics",
        "-fansi-escape-codes",
        "-g",
        "${file}",
        "-o",
        "${fileDirname}/${fileBasenameNoExtension}"
      ],
      "options": {
        "cwd": "${fileDirname}"
      },
      "problemMatcher": ["$gcc"],
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "detail": "Task generated by Debugger."
    }
  ],
  "version": "2.0.0"
}

注意:您可以在變數參考中深入了解 tasks.json 變數。

command 設定指定要執行的程式。在此案例中,為 clang++

args 陣列指定傳遞至 clang++ 的命令列引數。這些引數必須以編譯器預期的順序指定。

此工作會指示 C++ 編譯器採用作用中檔案 (${file})、編譯它,並在目前目錄 (${fileDirname}) 中建立輸出檔案 (-o 參數),其名稱與作用中檔案相同,但沒有副檔名 (${fileBasenameNoExtension})。此程序會建立 helloworld

label 值是您在工作清單中看到的內容,這取決於您的個人喜好設定。

detail 值是工作清單中工作的描述。更新此字串以將其與類似工作區分開來。

problemMatcher 值會選取輸出剖析器,以用於在編譯器輸出中尋找錯誤和警告。對於 clang++,$gcc 問題比對器會產生最佳結果。

從現在起,播放按鈕一律會從 tasks.json 讀取,以判斷如何建置和執行您的程式。您可以在 tasks.json 中定義多個建置工作,而標示為預設的工作就是播放按鈕使用的工作。如果您需要變更預設編譯器,您可以在命令面板中執行 [工作: 設定預設建置工作]。或者,您可以修改 tasks.json 檔案並移除預設值,方法是取代此區段

    "group": {
        "kind": "build",
        "isDefault": true
    },

以此

    "group": "build",

修改 tasks.json

您可以修改您的 tasks.json,以使用類似 "${workspaceFolder}/*.cpp" 而非 "${file}" 的引數來建置多個 C++ 檔案。這會建置目前資料夾中的所有 .cpp 檔案。您也可以修改輸出檔名,方法是將 "${fileDirname}/${fileBasenameNoExtension}" 取代為硬式編碼的檔名 (例如 "${workspaceFolder}/myProgram.out")。

偵錯 helloworld.cpp

若要偵錯您的程式碼,

  1. 返回 helloworld.cpp,使其成為作用中檔案。

  2. 在編輯器邊界上按一下或在目前行上使用 F9 來設定中斷點。

    screenshot of breakpoint in helloworld.cpp

  3. 從播放按鈕旁的下拉式清單中,選取 [偵錯 C/C++ 檔案]。

    Screenshot of play button drop-down

  4. 從系統上偵測到的編譯器清單中,選擇 [C/C++: clang++ 建置並偵錯作用中檔案] (您只需在第一次執行或偵錯 helloworld.cpp 時選擇編譯器)。

    Build and debug task

  5. 您會看到工作執行並將輸出列印到 [終端機] 視窗。

    Hello World Terminal Output

播放按鈕有兩種模式:[執行 C/C++ 檔案] 和 [偵錯 C/C++ 檔案]。預設值是上次使用的模式。如果您在播放按鈕中看到偵錯圖示,您可以選取播放按鈕進行偵錯,而不是選取下拉式功能表項目。

探索偵錯工具

在您開始逐步執行程式碼之前,讓我們先花點時間注意使用者介面中的幾個變更

  • 整合式終端機出現在原始碼編輯器的底部。在 [偵錯主控台] 索引標籤中,您會看到指出偵錯工具已啟動並執行的輸出。

  • 編輯器會醒目提示您在啟動偵錯工具之前設定中斷點的行

    Initial breakpoint

  • 活動列中的 [執行和偵錯] 檢視會顯示偵錯資訊。

  • 在程式碼編輯器的頂端,會出現偵錯控制面板。您可以抓取左側的點,在畫面上移動此面板。

    Debugging controls

逐步執行程式碼

現在您已準備好開始逐步執行程式碼。

  1. 選取偵錯控制面板中的 [逐步跳過] 圖示,讓 for (const string& word : msg) 陳述式醒目提示。

    Step over button

    [逐步跳過] 命令會跳過在建立和初始化 msg 變數時呼叫之 vectorstring 類別內的所有內部函式呼叫。請注意 [變數] 視窗中的變更。msg 的內容可見,因為該陳述式已完成。

  2. 再次按下 [逐步跳過] 以進階至下一個陳述式 (跳過執行以初始化迴圈的所有內部程式碼)。現在,[變數] 視窗會顯示迴圈變數的相關資訊。

  3. 再次按下 [逐步跳過] 以執行 cout 陳述式。

  4. 如果您願意,您可以持續按下 [逐步跳過],直到向量中的所有單字都已列印到主控台。但如果您很好奇,請嘗試按下 [逐步執行] 按鈕,以逐步執行 C++ 標準程式庫中的原始碼!

設定監看式

您可能會想要在程式執行時追蹤變數的值。您可以藉由在變數上設定監看式來執行此動作。

  1. 在 [監看式] 視窗中,選取加號並在文字方塊中輸入 word。這是迴圈變數的名稱。現在在逐步執行迴圈時檢視 [監看式] 視窗。

    Watch window

    注意:監看式變數的值僅在程式執行位於變數的範圍內時才可用。例如,對於迴圈變數,值僅在程式執行迴圈時才可用。

  2. 新增另一個監看式,方法是在迴圈之前新增此陳述式:int i = 0;。然後,在迴圈內,新增此陳述式:++i;。現在如同上一個步驟一樣,為 i 新增監看式。

  3. 您可以將滑鼠指標停留在任何變數上,同時暫停執行,以快速檢視其值。

    Mouse hover

使用 launch.json 自訂偵錯

當您使用播放按鈕或 F5 偵錯時,C++ 擴充功能會即時建立動態偵錯組態。

在某些情況下,您會想要自訂偵錯組態,例如指定在執行階段傳遞至程式的引數。您可以在 launch.json 檔案中定義自訂偵錯組態。

若要建立 launch.json,請從播放按鈕下拉式功能表中選擇 [新增偵錯組態]。

Add debug configuration play button menu

然後您會看到各種預先定義的偵錯組態的下拉式清單。選擇 [C/C++: clang++ 建置並偵錯作用中檔案]。 C++ 偵錯組態下拉式清單

VS Code 會建立 launch.json 檔案,其看起來如下所示

{
  "configurations": [
    {
      "name": "C/C++: clang++ build and debug active file",
      "type": "cppdbg",
      "request": "launch",
      "program": "${fileDirname}/${fileBasenameNoExtension}",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${fileDirname}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "lldb",
      "preLaunchTask": "C/C++: clang++ build active file"
    }
  ],
  "version": "2.0.0"
}

program 設定指定您要偵錯的程式。此處設定為作用中檔案資料夾 ${fileDirname} 和作用中檔案名稱 ${fileBasenameNoExtension},如果 helloworld.cpp 是作用中檔案,則會是 helloworldargs 屬性是在執行階段傳遞至程式的引數陣列。

根據預設,C++ 擴充功能不會將任何中斷點新增至您的原始碼,且 stopAtEntry 值設定為 false

stopAtEntry 值變更為 true,以讓偵錯工具在您開始偵錯時在 main 方法上停止。

請確定 preLaunchTask 值符合 tasks.json 檔案中建置工作的 label

從現在起,播放按鈕和 F5 會在啟動程式進行偵錯時從您的 launch.json 檔案讀取。

新增其他 C/C++ 設定

若要更進一步控制 C/C++ 擴充功能,請建立 c_cpp_properties.json 檔案,讓您可以變更設定,例如編譯器的路徑、包含路徑、要針對哪個 C++ 標準進行編譯 (例如 C++17) 等等。

透過從命令面板 (⇧⌘P (Windows、Linux Ctrl+Shift+P)) 執行命令 [C/C++: 編輯組態 (UI)] 來檢視 C/C++ 組態 UI。

Command Palette

這會開啟 [C/C++ 組態] 頁面。

C++ configuration

Visual Studio Code 會將這些設定放在 .vscode/c_cpp_properties.json 中。如果您直接開啟該檔案,它應該看起來如下所示

{
  "configurations": [
    {
      "name": "Mac",
      "includePath": ["${workspaceFolder}/**"],
      "defines": [],
      "macFrameworkPath": [
        "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
      ],
      "compilerPath": "/usr/bin/clang",
      "cStandard": "c11",
      "cppStandard": "c++17",
      "intelliSenseMode": "macos-clang-arm64"
    }
  ],
  "version": 4
}

只有當您的程式包含不在您的工作區或標準程式庫路徑中的標頭檔時,您才需要修改 [包含路徑] 設定。

編譯器路徑

擴充功能會使用 compilerPath 設定來推斷 C++ 標準程式庫標頭檔的路徑。當擴充功能知道在何處尋找這些檔案時,它可以提供智慧完成和 [跳到定義] 導覽等功能。

C/C++ 擴充功能會嘗試根據在您的系統上找到的內容,以預設編譯器位置填入 compilerPathcompilerPath 搜尋順序為

  • 您的 PATH 中已知編譯器的名稱。編譯器在清單中出現的順序取決於您的 PATH。
  • 然後搜尋硬式編碼的 Xcode 路徑,例如 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/

如需詳細資訊,請參閱 IntelliSense 設定文件

Mac framework 路徑

在 [C/C++ 組態] 畫面上,向下捲動並展開 [進階設定],並確認 [Mac framework 路徑] 指向系統標頭檔。例如:/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks

疑難排解

編譯器和連結錯誤

錯誤 (例如 undefined _mainattempting to link with file built for unknown-unsupported file format 等等) 最常見的原因是當您開始建置或開始偵錯時,helloworld.cpp 不是作用中檔案。這是因為編譯器嘗試編譯的不是原始碼,而是您的 launch.jsontasks.jsonc_cpp_properties.json 檔案。

如果您看到提及 "C++11 extensions" 的建置錯誤,則您可能尚未更新 tasks.json 建置工作以使用 clang++ 引數 --std=c++17。根據預設,clang++ 使用 C++98 標準,其不支援 helloworld.cpp 中使用的初始化。請務必將 tasks.json 檔案的完整內容取代為 執行 helloworld.cpp 區段中提供的程式碼區塊。

終端機不會啟動以進行輸入

在 macOS Catalina 和更新版本中,您可能會遇到無法輸入的問題,即使在設定 "externalConsole": true 之後也是如此。終端機視窗會開啟,但實際上不允許您輸入任何內容。

此問題目前已在 #5079 中追蹤。

因應措施是讓 VS Code 啟動終端機一次。您可以藉由在 tasks.json 中新增和執行此工作來執行此動作

{
  "label": "Open Terminal",
  "type": "shell",
  "command": "osascript -e 'tell application \"Terminal\"\ndo script \"echo hello\"\nend tell'",
  "problemMatcher": []
}

您可以使用 [終端機] > [執行工作...] 並選取 [開啟終端機] 來執行此特定工作。

一旦您接受權限要求,外部主控台應該會在您偵錯時出現。

後續步驟