在 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++ 擴充功能。您可以透過在擴充功能檢視中搜尋「C++」來安裝 C/C++ 擴充功能(⇧⌘X (Windows, Linux Ctrl+Shift+X))。

    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 設定。從編譯器下拉式選單中,選取 Use clang++ 進行設定。更多資訊請參閱 IntelliSense 設定文件

執行 helloworld.cpp

請記住,C++ 擴充功能使用您安裝在電腦上的 C++ 編譯器來建置程式。在嘗試於 VS Code 中執行及偵錯 helloworld.cpp 之前,請務必先安裝好 C++ 編譯器(如 Clang)。

  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++ 擴充功能會在專案的 .vscode 資料夾中建立 tasks.jsontasks.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 以建置多個 C++ 檔案,使用 "${workspaceFolder}/*.cpp" 作為參數,而不是 "${file}"。這會建置您目前資料夾中的所有 .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. 選取偵錯控制面板中的逐步跳過 (Step over) 圖示,使 for (const string& word : msg) 陳述式被反白顯示。

    Step over button

    逐步跳過指令會跳過在建立並初始化 msg 變數時所觸發的 vectorstring 類別內部的所有函式呼叫。請注意變數視窗中的變化。由於該陳述式已完成,msg 的內容現在可見。

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

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

  4. 如果您願意,可以持續按下逐步跳過,直到向量中的所有單字都列印到主控台為止。但如果您感到好奇,請嘗試按下逐步執行 (Step Into) 按鈕,以逐步追蹤 C++ 標準函式庫中的原始程式碼!

設定監看式 (Watch)

您可能希望在程式執行時持續追蹤某個變數的值。您可以透過對該變數設定監看式 (Watch) 來達成。

  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 檔案,這讓您可以變更編譯器路徑、包含路徑 (Include paths)、要編譯的 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
}

僅當您的程式包含不在您的工作區或標準函式庫路徑中的標頭檔時,才需要修改包含路徑 (Include path) 設定。

編譯器路徑

擴充功能使用 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 _main,或 attempting to link with file built for unknown-unsupported file format 等)最常見的原因,是您在開始建置或偵錯時,helloworld.cpp 並非作用中檔案。這是因為編譯器嘗試編譯非原始程式碼的內容,例如您的 launch.jsontasks.jsonc_cpp_properties.json 檔案。

如果您看到提及「C++11 擴充功能」的建置錯誤,可能是因為您沒有將 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": []
}

您可以透過終端機 > 執行任務... 並選擇 開啟終端機來執行此特定任務。

一旦您接受權限要求,進行偵錯時外部主控台應該就會正常顯示。

後續步驟

  • 探索 VS Code 使用者指南
  • 查看 C++ 擴充功能總覽
  • 建立新的工作區,將您的 .json 檔案複製到其中,為新的工作區路徑、程式名稱等調整必要的設定,然後開始編寫程式碼吧!
© . This site is unofficial and not affiliated with Microsoft.