VSCode上c++编译调试运行配置指南

There is no strife, no prejudice, no national conflict in outer space as yet. Its hazards are hostile to us all. Its conquest deserves the best of all mankind, and its opportunity for peaceful cooperation may never come again. But why, some say, the Moon? Why choose this as our goal? And they may well ask, why climb the highest mountain? Why, 35 years ago, fly the Atlantic? Why does Rice play Texas?

本文面向小白!!!

1.准备环境

首先我们要明确,VSC是一个编辑器(editor ),而非IDE(集成开发环境),不具备编译器(compiler)以及其他功能,因此我们需要安装编译器。

  • 下载Clang,选Pre-Built Binaries中的Windows (64-bit),不需要下.sig文件

安装 Clang :添加环境变量时,选 Add LLVM to the system PATH for all users (第三项也行)。

  • 下载MinGW-W64,选最新版本中的x86_64-posix-seh

安装MinGW-w64:下下来的是一个7z的压缩包,把东西全部复制到Clang的文件夹里去(主要为了之后操作方便),它们会无冲突合并。

验证安装

运行终端,输入clanggcc,应该会提示no input files而不是不是内部命令或外部命令或者无法将‘clang’项识别为cmdlet、函数、脚本文件或可运行程序的名称。如果是不是内部命令或外部命令,说明clang.exe在的文件夹没有在环境变量中,要加到Path里才行。

输入clang -vgcc -v可以显示出各自的版本。如果显示出来的版本与刚下的不同/更老,说明Path里原本有老版本的编译器,可能是安装其它IDE时装上的,则需要去掉。

安装VSC扩展(extension)

必装
  • C/C++:又名cpptools,提供Debug和Format功能
  • C/C++ Clang Command Adapter:提供静态检测(Lint)功能
  • Code Runner:右键即可编译运行单文件,很方便,但无法Dubug
选装
  • Bracket Pair Colorizer:彩虹花括号
  • vscode-icons:为VSCode里的不同文件类型提供相应的图标,F1->icons激活
  • Include Autocomplete:提供头文件名字的补全,不过用处不大,而且现在cpptools已经自带这个功能
  • C/C++ Snippets:Snippets即重用代码块
  • One Dark Pro:皮肤主题
  • vscode-clangd:和Adapter二选一,出得比Adapter晚,下载量也低,但却是LLVM官方出的
  • Clang-Format:只有想自定义代码风格时才装,比如大括号不换行
不装
  • GBKtoUTF8:把GBK编码的文档转换成UTF8编码的,此扩展可能有严重的bug
  • C++ Intellisense:用的是gtags,效果非常非常一般

FAQ

  • Q: 为什么要装Clang?
    A: 错误提示更友好。以及:Clang 比 GCC 好在哪里?
  • Q: 为什么既要装Clang又要装MinGW?
    A: Clang没有stdio.h等头文件。
  • Q: MSVC integration install failed / unable to find a Visual Studio installation…
    A: Win下的Clang默认用的是MSVC的后端。如果完全按照本文接下来的操作,不用管这个提示

2. 配置.json文件等

  • 创建一个文件夹(工作区),在其中新建文件夹.vscode
  • .vscode中创建launch.jsontasks.jsonsettings.json

launch.json

// https://github.com/Microsoft/vscode-cpptools/blob/master/launch.md
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch", // 配置名称,将会在启动配置的下拉菜单中显示
            "type": "cppdbg", // 配置类型,cppdbg对应cpptools提供的调试功能;可以认为此处只能是cppdbg,不变色是正常现象
            "request": "launch", // 请求配置类型,可以为launch(启动)或attach(附加),不变色是正常现象
            "program": "${fileDirname}/${fileBasenameNoExtension}.exe", // 将要进行调试的程序的路径
            "args": [], // 程序调试时传递给程序的命令行参数,一般设为空即可
            "stopAtEntry": false, // 设为true时程序将暂停在程序入口处,相当于在main上打断点
            "cwd": "${workspaceFolder}", // 调试程序时的工作目录,此为工作区文件夹;改成${fileDirname}可变为文件所在目录
            "environment": [], // 环境变量
            "externalConsole": true, // 为true时使用单独的cmd窗口,与其它IDE一致;18年10月后设为false可调用VSC内置终端
            "internalConsoleOptions": "neverOpen", // 如果不设为neverOpen,调试时会跳到“调试控制台”选项卡
            "MIMode": "gdb", // 指定连接的调试器,可以为gdb或lldb。但我没试过lldb
            "miDebuggerPath": "gdb.exe", // 调试器路径,Windows下后缀不能省略,Linux下则不要
            "setupCommands": [
                { // 模板自带,好像可以更好地显示STL容器的内容,具体作用自行Google
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": false
                }
            ],
            "preLaunchTask": "Compile" // 调试会话开始前执行的任务,一般为编译程序。与tasks.json的label相对应
        }
    ]
}

tasks.json

// https://code.visualstudio.com/docs/editor/tasks
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Compile", // 任务名称,与launch.json的preLaunchTask相对应
            "command": "clang", // 要使用的编译器,C++用clang++;用MinGW就分别是gcc和g++,但注意把--target那条删去
            "args": [
                "${file}",
                "-o", // 指定输出文件名,不加该参数则默认输出a.exe,Linux下默认a.out
                "${fileDirname}/${fileBasenameNoExtension}.exe",
                "-g", // 生成和调试有关的信息
                "-Wall", // 开启额外警告
                "-static-libgcc", // 静态链接libgcc,一般都会加上
                "--target=x86_64-w64-mingw", // clang的默认target为msvc,不加这一条就会找不到头文件;用gcc或者Linux则掉这一条
                // "-std=c11", // C++最新标准为c++17,或根据自己的需要进行修改
            ], // 编译命令参数
            "type": "process", // process是vsc把预定义变量和转义解析后直接全部传给command;shell相当于先打开shell再输入命令,所以args还会经过shell再解析一遍
            "group": {
                "kind": "build",
                "isDefault": true // 不为true时ctrl shift B就要手动选择了
            },
            "presentation": {
                "echo": true,
                "reveal": "always", // 执行任务时是否跳转到终端面板,可以为always,silent,never。具体参见VSC的文档
                "focus": false, // 设为true后可以使执行task时焦点聚集在终端,但对编译C/C++来说,设为true没有意义
                "panel": "shared" // 不同的文件的编译信息共享一个终端面板
            },
            // "problemMatcher":"$gcc" // 此选项可以捕捉编译时终端里的报错信息;本文用的是clang,开了可能会出现双重报错信息;只用cpptools可以考虑启用
        }
    ]
}

settings.json

  • 放到“用户设置”里可以覆盖全局设置,否则只在当前工作区才有效。
  • Linux 下去掉--target参数
{
    "files.defaultLanguage": "c", // ctrl+N新建文件后默认的语言
    "editor.formatOnType": true, // (对于C/C++)输入分号后自动格式化当前这一行的代码
    "editor.suggest.snippetsPreventQuickSuggestions": false, // clangd的snippets有很多的跳转点,不用这个就必须手动触发Intellisense了
    "editor.acceptSuggestionOnEnter": "off", // 我个人的习惯,按回车时一定是真正的换行,只有tab才会接受Intellisense
    // "editor.snippetSuggestions": "top", // (可选)snippets显示在补全列表顶端,默认是inline

    "code-runner.runInTerminal": true, // 设置成false会在“输出”中输出,无法输入
    "code-runner.executorMap": {
        "c": "cd $dir && clang '$fileName' -o '$fileNameWithoutExt.exe' -Wall -g -O2 -static-libgcc --target=x86_64-w64-mingw -std=c11 && &'$dir$fileNameWithoutExt'",
        "cpp": "cd $dir && clang++ '$fileName' -o '$fileNameWithoutExt.exe' -Wall -g -O2 -static-libgcc --target=x86_64-w64-mingw -std=c++17 && &'$dir$fileNameWithoutExt'"
        // "c": "cd $dir && clang $fileName -o $fileNameWithoutExt.exe -Wall -g -O2 -static-libgcc --target=x86_64-w64-mingw -std=c11 && $dir$fileNameWithoutExt",
        // "cpp": "cd $dir && clang++ $fileName -o $fileNameWithoutExt.exe -Wall -g -O2 -static-libgcc --target=x86_64-w64-mingw -std=c++17 && $dir$fileNameWithoutExt"
    }, // 控制Code Runner命令;未注释的仅适用于PowerShell(Win10默认),文件名中有空格也可以编译运行;注释掉的适用于cmd(win7默认),也适用于PS,文件名中有空格时无法运行
    "code-runner.saveFileBeforeRun": true, // run code前保存
    "code-runner.preserveFocus": true, // 若为false,run code后光标会聚焦到终端上。如果需要频繁输入数据可设为false
    "code-runner.clearPreviousOutput": false, // 每次run code前清空属于code runner的终端消息,默认false
    "code-runner.ignoreSelection": true, // 默认为false,效果是鼠标选中一块代码后可以单独执行,但C是编译型语言,不适合这样用

    "C_Cpp.clang_format_sortIncludes": true, // 格式化时调整include的顺序(按字母排序)
    "C_Cpp.errorSquiggles": "Disabled", // 因为有clang的lint,所以关掉
    "C_Cpp.autocomplete": "Disabled", // 同上;这几条也可以考虑放到全局里,否则很多错误会报两遍,cpptools一遍clangd一遍
    "C_Cpp.suggestSnippets": false, // 同上
}

compile_flags.txt

此文件是clangd为了提供必须的功能所使用的,效果和直接把它们传递给clang.exe差不多。感兴趣的可以参见:https://clangd.github.io/

-Wall
--target=x86_64-w64-mingw
#-std=c++17
#如果写C++,就去掉上面一行最开头的井号

此文件对于本文的配置是必须创建的,且一定要是这个文件名,大小写要也一样。位置需要在工作区的磁盘根目录(例:C:\compile_flags.txt)。

clangd只会使用离要评估的文件最近的一个compile_flags.txt。可以在工作区目录创建此文件来覆盖根目录的配置,好处是可以分开配置,缺点是这样代码列表里会多一个文件。将此文件放在根目录是确保--target参数是存在的,是最后的fallback,否则就报找不到头文件的错误,Lint也几乎没用。

但是由于C和C++都会使用.h作为头文件,如果不加任何std.c.cpp能正确Lint,但是.h会使用C的模式。这是fallback不能解决的问题。

c_cpp_properties.json

如果自己编写了头文件又不在workspaceFolder下,或是使用别人的库,就需要手动创建这个文件放到.vscode下。

  • 库的路径要加到includePath和browse里
  • 如果需要递归包含,末尾加/**
  • 此json不允许有注释
  • compilerPath必须是MinGW的完整路径,精确到gcc.exe,否则会提示找不到头文件;Linux下是/usr/bin/gcc
  • Windows下的目录分隔符为反斜杠,原本应使用两个反斜杠来转义,但直接用斜杠这里也接受
  • 除了配置这个文件,还需要进行别的操作。一部分可以参考下文的“多文件编译”

一些说明

json是一种数据交换格式,在这里就是用作配置文件。VSC和各个扩展会读取json中的条目,来决定某些功能和行为。

条目和API差不多。扩展开发者会把允许修改的选项“告诉”VSC,各个扩展的安装页面都有写。作为使用者,输入的时候VSC会提示你哪些是可用的。

为什么要往json里写这么多的东西?因为VSC本身并没有对C语言特别优待。

以$开头的是VSC预定义的变量,具体参见:Variables Reference。比如$file在实际运行时会替换成当前打开的文件名。

3. 代码,编译,调试

Alt+Shift+F可以格式化代码,第一次用的时候右下角会提示选择格式化提供程序,如果你喜欢大括号换行就选C/C++,喜欢不换行就选vscode-clangd。出现Intellisense的时候按tab可以补全代码。打出snippets时会出现多个跳转点,按tab可以跳到下一个去。

停止输入一小段时间后就会有Lint,扩展会给一些建议性的warnings(比如声明了变量但不使用)。如果觉得不爽,也有方法不让它提示,比如去掉-Wall就会少一些。找好参数后可以用#pragma GCC diagnostic ignored或者加到各种Flags里。

Ctrl+Shift+B单纯编译,按F5为编译加运行加调试;本来Ctrl+F5为运行但不调试,但现在cpptools暂不支持,还是会调试。开始调试后,按F11可以一步一步进行,箭头所指的那行代码就是下一步要运行的代码F5是一直运行到下一个断点,右键某一行代码可以选择一直运行到指定的那一行。

强烈建议不要把F5当作编译来使用,因为有的bug只会产生警告,不会阻止编译。

左边调试栏可以看到变量的值,自动栏没有的可以手动添加:在代码里选中要监视的表达式,点右键有选项可以直接添加到Watch里,复杂的才需要手打。把鼠标放到变量上可以看到变量的值,但是只能识别简单的表达式。栈帧对于观察递归很有用。栈溢出和段错误时还可以抓取“异常”,自动跳转到出错的行。

特别的,对于数组:C语言的数组经过函数传递以后会退化为指针,直接添加表达式就只能看到第一个元素。此时可以强制转换成指向固定大小的数组指针再解引。例如int arr[10]传进函数里后就变成了int* arr,此时可以添加*(int(*)[10])这个表达式,就能看到完整的数组了,但长度必须是写死的。或者简单的程序用全局变量数组就能一直看到了。

快捷键:vscode: Visual Studio Code 常用快捷键 – 志文工作室。英文文档中当然有快捷键的说明,还有Cheet Sheet可以看,而且英文文档会更新。

Code Runner

如果你不需要调试,可以直接右键选run code,或者点右上角的播放按钮。如果在终端里运行,可以输入数据,但是少了显示时间的功能;在“输出”中则上面两项相反。

它还可以在非工作区内编译运行程序,不过默认用的是gcc,除非把executorMap放到全局设置里。task和Code Runner还有一点不同:working directory。前者是你打开的文件夹,后者是文件所在的文件夹。当然它们也都可以自己修改。

其实Code Runner只是代替你手动输命令,功能并不强,算是适用场景不同。

中文和乱码

我死都不会去搞GBK和UTF-8的转换的,全英文解决一切。

找不到头文件

  • 没有创建compile_flags.txt
  • gcc不在Path里
  • 手动配置了c_cpp_properties.json且包含的路径不正确(未创建此文件不用管)
  • clang的默认target为msvc,需要加--target=x86_64-w64-mingw这个参数才行
  • 重启试试

引用

Visual Studio Code 如何编写运行 C、C++ 程序? – 谭九鼎的回答 – 知乎 https://www.zhihu.com/question/30315894/answer/154979413

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据