前一篇文章中,我们讲述了如何利用 VSCode 软件来搭建一个 C 语言的开发环境,这个 C 语言的开发环境和我们之前《乐创 C 语言教程》里面所讲述的有很大差别,这些差别在我看来对于那些已经了解 C 语言命令行编译过程的童鞋来说是一个福音,因为它可以大大地省去我们每次编译时需要输入的命令。当然,如果你一开始学习 C 语言就使用这样省事的工具,对你自身了解 C 语言的编译步骤以及文件依赖关系来说,并不是一件好事。所以,我也衷心希望刚刚接触 C 语言的童鞋还是按照我们《乐创 C 语言教程》里面所讲述的工具来学习 C 语言,等到了解了这些编译的过程,再来搭建这个 VSCode IDE。
VSCode 的预定义变量
我们上一篇文章中讲述了如何将 MinGW 工具嵌入到 VSCode 文本编辑器中,在这个配置的过程中,我们只需要通过修改 VSCode 生成的“luanch.json”和“task.json”两个 JSON 文件中的特定字段,就可以实现开发环境的搭建。那么这里可能就会有读者疑惑了,这些字段既然是 VSCode 提供的,那么有没有一张详细罗列这些参数的表格,以备自己的不时之需。这个答案是肯定的,VSCode 编辑器的官网有相关的文档专门来解释这些 JSON 文档中的关键字段,详情可以参考 VSCode 官方文档:
https://code.visualstudio.com/docs/editor/variables-reference。这里我们也来简单地讲述下。
图 1 task.json
如图 1 所示的“task.json”中的内容。这个文件中的后缀名是“.json”,关于“json”我们在这里简单介绍下,JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的 js 规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
JSON 文件的内容都是以“键 - 值对”的形式存在的,键值对是一种高级语言广泛支持的符合数据类型,在某些高级语言中也被称为“字典”,其内部的每一个数据项的结构为:
索引(键)”: “内容(值)
在 VSCode 中,它提供两个用以配置 VSCode 任务的文件,“launch.json”以及”tasks.json”。“launch.json”为调试配置文件,这里面指定了启动调试之后的调试器命令,以及调试之前的任务。这里我们可以将编译任务在这里指出。
“task.json”为编译任务配置文件,这里指出了编译任务的命令以及具体参数。
再仔细观察图 1,我们发现其中很多字段对应的变量是以“${变量名}”形式出现的,这些变量名其实在 VSCode 中是被事先定义好的,有点类似于我们操作系统中的环境变量,其中部分变量的具体解释如下:
$ {workspaceFolder} - 在 VS Code 中打开的文件夹的路径
$ {workspaceFolderBasename} - 在 VS Code 中打开的文件夹名称,不带任何斜杠(/)
$ {file} - 当前打开的文件
$ {fileWorkspaceFolder} - 当前打开的文件的工作区文件夹
$ {relativeFile} - 当前相对于打开的文件 workspaceFolder
$ {relativeFileDirname} - 当前打开的文件相对于的目录名 workspaceFolder
$ {fileBasename} - 当前打开的文件的基本名称
$ {fileBasenameNoExtension} - 当前打开的文件的基本名称,没有文件扩展名
$ {fileDirname} - 当前打开的文件的目录名
$ {fileExtname} - 当前打开的文件的扩展名
$ {cwd} - 启动时任务运行程序的当前工作目录
$ {lineNumber} - 活动文件中当前选择的行号
$ {selectedText} - 活动文件中的当前选定文本
$ {execPath} - 正在运行的 VS Code 可执行文件的路径
$ {defaultBuildTask} - 默认构建任务的名称
$ {pathSeparator} - 操作系统用来分隔文件路径中的组件的字符
编译多个源文件
到目前为止,我们讲述的 VSCode C 语言编译器貌似还是只支持单一源文件的编译。那么如果我有两个,三个甚至多个源文件的编译,是不是需要像之前讲述的那样写 Makefile?
要回答这个问题,最好的办法就是立即动手写代码,我们可以在同一个文件夹中创建一个“a.c”和“a.h”文件,这个文件中只实现输出一句“Hello world”的功能。定义好函数之后,我们在“main.c”文件中去包含这个“a.h”,接着去调用 a.h 中声明的打印函数,具体代码如图 2 所示。
图 2 新增的“a.c”和“a.h”
//main.c
#include
#include "a.h"
int main(void) {
int number;
printf("input:n");
scanf("%d", &number);
printHello();
if((number > 1) && (number <= 100)) {
printf("Smalln");
} else if((number > 101) && (number <= 200)) {
printf("Middlen");
} else {
printf("Largen");
}
return 0;
}
//a.c
#include
#include "a.h"
void printHello(void) {
printf("Hello world");
}
//a.h
#ifndef __A_H_
#define __A_H_
void printHello(void);
#endif
然后,我们在上一篇文章编写的“launch.json”和“task.json”文件基础上进行调试。结果 VSCode 抛出了如图 3 所示的错误。
图 3 编译输出错误
我们在终端上可以看到其出错的原因,归根到底是因为没有找到“main.c”文件中的“printHello”函数定义,这个原因是因为编译器其实是没有编译 a.c 这个源文件。那如何才能添加对这个源文件的编译呢?我们再仔细来看下,终端提示的错误中,在执行 “D:softwaremingw64bingcc.exe '-g', 'C:Users1DesktopNumCalmain.c', '-o', 'C:Users1DesktopNumCalmain.exe'”语句时退出的,这句语句其实是我们在“task.json”文件中定义的。
图 4 task.json
如图 4 红框中圈出的便是具体的编译命令,因此我们在这个编译命令中只需要增加对“a.c”文件的编译即可。如图 5 所示,在原有的“task.json”文件基础上,增加了对“a.c”文件的编译。
图 5 “task.json”中,增加对 a.c 文件的编译
//task.json
{
"version": "2.0.0",
"label": "C/C++: gcc.exe build active file",
"command": "gcc",
"args": [
"-g",
"${file}",
"${fileDirname}\a.c", // 增加对 a.c 文件的编译
"-o",
"${fileDirname}\${fileBasenameNoExtension}.exe"
],
"problemMatcher": [
"$gcc"
],
"group": "build",
"detail": "compiler: D:\software\mingw64\bin\gcc.exe",
}
//launch.json
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "build and debug", // 配置名称,将会在启动配置的下拉菜单中显示
"type": "cppdbg", // 配置类型,这里只能为 cppdbg
"request": "launch", // 请求配置类型,可以为 launch(启动)或 attach(附加)
"program": "${fileDirname}/${fileBasenameNoExtension}.exe",
// 将要进行调试的程序的路径
"args": [], // 程序调试时传递给程序的命令行参数,一般设为空即可
"stopAtEntry": false, // 设为 true 时程序将暂停在程序入口处,一般设置为 false
"cwd": "${fileDirname}", // 调试程序时的工作目录,一般为${fileDirname}即代码所在目录
"environment": [],
"externalConsole": true, // 调试时是否显示控制台窗口,一般设置为 true 显示控制台
"MIMode": "gdb",
"miDebuggerPath": "D:/software/mingw64/bin/gdb.exe", // miDebugger 的路径,注意这里要与 MinGw 的路径对应
"preLaunchTask": "gcc", // 这里需要添加一个参数,
// 调试会话开始前执行的任务,一般为编译程序,c++为 g++, c 为 gcc
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
接着,我们切换回“main.c”,再点击调试代码,此时 VSCode 就可以正常进入调试界面了。如图 6 所示。
图 6 VSCode 调试界面