什麼是 VScode
Visual Studio Code(VScode) 是一個輕量級但功能強大的源代碼編輯器,可在桌面上運行,並且可用於Windows,macOS和Linux。VS code 包括一整套系統:比如內部支持 JavaScript,TypeScript 和 Node.js,能夠進行多種語言擴展(如 C++,C#,Java,Python,PHP,Go),也能夠進行多種運行擴展(如.NET 和 Unity)。
簡單的說就是 notepad+compiler+extensions。
VScode 特點
之前我也沒有用過 VScode,一直用的都是 VS,Qt 這樣的工具,但是真的用上 VScode 之後,就發現其實如果瞭解一下 VScode 的使用方式的話,這個工具使用起來還是比較簡單的。主要的特點有:
- 跨平臺。VScode 能夠運行在 macOS,Linux,Windows操作系統上
- 輕便靈活。VScode 在最初安裝只包含了開發流程共享的基本組件,只實現了包括編輯器,文件管理,窗口管理,首選項設置, JavaScript/TypeScript 語言服務和 Node.js 調試器等基本功能,此時的 VScode 就是一個代碼編輯器
- 可擴展。VS Code 擴展可以添加對以下內容的支持:
- 語言:C++,C#,Go,Java,Python
- 工具:ESLint,JSHint,PowerShell
- 調試器:Chrome,PHP XDebug。
- 鍵盤映射:Vim,Sublime Text,IntelliJ,Emacs,Atom,Visual Studio,Eclipse
hello world
這一部分以實際例子來說明 VScode 的使用方法,運行平臺爲 Linux,編程語言爲 C/C++。
前提條件
- GCC
- GDB
- VScode
建立工程
VScode 不同於 VS,Qt 可以直接新建工程,在 VScode 中需要自己手動建立工程目錄。其實就是新建一個文件夾,也不需要在文件夾中包含類似 CMakeLists.txt 的文件,因爲這樣的文件會在之後自動生成。
然後打開 VScode,利用菜單欄中的 File 打開剛剛新建的新文件夾,該文件夾就是工程目錄。
代碼編輯
此時先寫一個簡單的 hello world 程序:
// main.cpp
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello world!"<<endl;
return 0;
}
.vscode
默認情況下,在工程目錄中會自動生成隱藏目錄 .vscode,該文件中主要包含三個文件:
- task.json(編譯器構建設置)
- launch.json(調試器設置)
- c_cpp_properties.json(編譯器路徑和IntelliSense設置)
如果如果你此時沒有看到該隱藏文件夾也沒有關係,之後你構建上述三個文件中的任何一個文件都會生成該隱藏文件夾。
從這裏來看,該隱藏文件夾有點像是 Qt 中的 .pro 文件。
task.json
既然代碼已經寫完了,接下來就是 build task 了。
在主菜單中,選擇 Terminal > Configure Default Build Task。出現一個下拉列表,顯示用於C++編譯器的各種預定義構建任務。選擇C/C++: g++ build active file。
這將在 .vscode 文件夾中創建 tasks.json 文件,並在編輯器中將其打開。tasks.json 文件應類似於以下 JSON:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "g++ build active file",
"command": "/usr/bin/g++",
"args": [
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "/usr/bin"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
此時可以看到隱藏文件目錄 .vscode 自動被創建。上邊的參數有:
- Command: 指定要運行的程序,在這種情況下是 g++
- Args: 指定將傳遞給 g++ 的命令行參數。參數必須按照編譯器期望的順序指定
- Label: 就是在任務列表中看到的值,可以隨意命名
- Group: "isDefault": true指定當按Ctrl+Shift+B時將運行此任務。此屬性僅僅是爲了便利性,如果將其設置爲false,仍然可以通過terminal菜單中的Tasks: Run Build Task運行
此任務告訴 g++ 獲取活動文件(${file}),對其進行編譯,然後在當前目錄(${fileDirname})中創建一個與活動文件同名但沒有擴展名(${fileBasenameNoExtension)})的可執行文件,示例爲 main。
運行程序
- 返回 main.cpp,任務將構建活動文件。
- 要運行 tasks.json 中定義的構建任務,按 Ctrl+Shift+B 或從 terminal 菜單中選擇 Run Build Task。
- 任務啓動時,會看到集成終端面板出現在源代碼編輯器下方。任務完成後,終端將顯示編譯器的輸出,指示構建成功還是失敗。
- 使用 + 按鈕創建一個新終端,這將在 WSL 上下文中運行一個 bash 終端,並以當前文件夾作爲工作目錄。
- 此時可以在終端運行 main。
launch.json
此時只是構建了任務,生成了可執行程序,但是想要調試的話還需要用到 launch.json 來設置調試器的相關內容。
- 在菜單欄中選擇 Run > Add Configuration...,然後選擇 C++ (GDB/LLDB)
- 然後將出現各種預定義調試配置的下拉列表。選擇 g++ build and debug active file
- VS Code 創建 launch.json 文件,在編輯器中打開爲:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++ - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "g++ build active file",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
上邊的參數中:
- Program: 指定要調試的程序。這裏被設置爲活動文件文件夾${fileDirname}和沒有擴展名的活動文件名${fileBasenameNoExtension},如果helloworld.cpp是活動文件,則爲helloworld
- 默認情況下,C++擴展不會在源代碼中添加任何斷點,因此stopAtEntry值設置爲false。stopAtEntry值爲true時,調試器啓動調試時遇到main方法停止
調試代碼
- 返回 main.cpp,使其成爲活動文件
- 按 F5 或從主菜單中選擇 Run > Start Debugging。此時用戶界面會發生一些更改:
- 集成終端出現在源代碼編輯器的底部。在 Debug Output 選項卡中,可以看到指示調試器已啓動並正在運行
- 編輯器突出顯示main方法中的第一條語句。這是 C++ 擴展自動設置的斷點
- 左側的 run 視圖顯示調試信息
- 在代碼編輯器的頂部,將顯示一個調試控制面板
調試控制面板的使用方法與其它 IDE 基本相同。
WATCH
在調試的時候,左側邊欄會出現一個 WATCH 的欄目,利用 WATCH 可以實現對變量跟蹤:
- 在 WATCH 窗口中,單擊加號,然後在文本框中鍵入 var(變量名),此時可以逐步調試程序,可以看出該變量一直顯示在 WATCH
- 在斷點開啓時,要快速查看任何變量的值,可以使用鼠標指針懸停在其上
c_cpp_properties.json
編程中通常會使用到一些第三方庫,如果要用到第三方庫的頭文件的話,就需要在該文件中添加頭文件目錄,不過此時添加的頭文件目錄只是爲了編程更加方法,實現智能感知,而並沒有真正的鏈接。
真正的鏈接需要在 task.json 文件中設置參數,才能夠正常編譯。
可以通過從 Command Palette (Ctrl+Shift+P) 運行命令 C/C++: Edit Configurations (UI) 來查看 C/C++ 配置 UI。
- 當在此處進行更改時,VS Code會將更改寫入.vscode文件夾中的c_cpp_properties.json文件中。
- 僅當程序包含不在工作空間或標準庫路徑中的頭文件時,才需要修改includepath設置。
外部庫鏈接的實例
當需要進行外部庫鏈接時,至少需要在 tasks.json 中進行頭文件包含和外部庫鏈接,如果需要使用代碼提示等智能感知功能的話,還需要修改 c_cpp_properties.json 文件。
以下用一個 C 語言的實例來簡單說明需要進行外部庫鏈接的情況(這裏的靜態庫是利用這篇文章中的文件生成的):
// main.c
#include <stdio.h>
#include <node.h>
int main()
{
printf("insert a node from tail\n");
ND *head = createListTail();
// printf("insert a node from head\n");
// ND *head = createListHead();
printf("traval all node using next\n");
travalListNext(head);
printf("insert a node\n");
insertList(head,10);
travalListNext(head);
// printf("traval all node using pre\n");
// travalListPre(head);
printf("calculate the length of list\n");
int length = lenList(head);
printf("The length of list is %d\n",length);
printf("search a node in list from single direction\n");
ND *pp = searchNodeSdir(head,10);
printf("The search node is %d\n",pp->data);
// printf("search a node in list from both direction\n");
// pp = searchNodeBdir(head,10);
// printf("The search node is %d\n",pp->data);
printf("delete a node\n");
deleteNode(pp);
travalListNext(head);
printf("sort list by swap data\n");
sortSwapData(head,9);
travalListNext(head);
// printf("sort list by swap pointer\n");
// sortSwapPointer(head,9);
// travalListNext(head);
printf("destroy list\n");
destroyList(head);
}
// tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "gcc build active file",
"command": "/usr/bin/gcc",
"args": [
"-g",
"${file}",
"-I",
"~/libr",
"-L",
"~/libr",
"-l",
"node",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "/usr/bin"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
// launch.json
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "gcc - Build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "gcc build active file",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
// c_cpp_properties.json
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"~/libr/**"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}
- 該工程需要的編譯器是 gcc,而不是 g++
- 如果在 c_cpp_properties.json 文件中沒有爲 includePath 參數添加頭文件,則在 main.c 文件中的 #include <node.h> 會出現下劃線,也就是說編輯器找不到該函數,但是實際是能夠編譯的。
- 在 tasks.json 文件中添加了一些參數,如 -I,-L,-l 等,這些參數包含了頭文件和外部鏈接庫。
- 不需要調試的話,可以不用添加 launch.json 文件
一個常見錯誤
當開始構建或調試時,xxx.cpp 不是活動文件時,最常見的錯誤原因(如 undefined _main 或 attempting to link with file built for unknown-unsupported file format 等)。這是因爲編譯器正在嘗試編譯不是源代碼的內容(如 launch.json,tasks.json 或 c_cpp_properties.json 文件)。
因此,運行任務時一定要保證當前活動文件爲包含 main 函數的文件。