在vscode上建立多文件c语言工程记录
vsvode是一款非常灵活的编译环境,其依靠c语言插件进行C语言编程,由于没有版权费用现在越发普及,这里是描述归纳了我在学习在vscode中建立多文件的c项目过程中的一些坑和总结。欢迎交流。
0、名词解释
在这里,我将做一个索引主要用来解释在下文中可能会遇到的专业名词。
(1)核心工具链类
这些东西主要用来编译,可以将他们理解为一个翻译器,目的是将C语言源文件翻译为电脑能够识别的二进制文件。
gcc:这个是编译器
make / mingw32-make: 这个是编译器的编译指令
gdb: 这个是调试工具,在vscode中可以通过按F5让程序暂停,单步执行
(2)关键配置文件
-
Makefile:构建规则脚本。告诉
make源文件在哪、头文件在哪、怎么编译、怎么链接。 -
tasks.json:VSCode 的任务配置。在 VSCode 里定义“任务”,比如调用
gcc或make。 -
launch.json:VSCode 的调试配置。告诉 VSCode “用哪个调试器(gdb)去调试哪个程序(
build/main.exe)”,通常配合F5使用。 -
c_cpp_properties.json:智能感知配置。专门给编辑器(IntelliSense)看的,让它知道去哪里找头文件,代码补全和“跳转到定义”才能正常工作
(3)一些概念
-
Code Runner:一个 VSCode 扩展,让你一键运行单文件代码。但它是多文件项目的“杀手”,因为它只会编译当前文件,不理会你的头文件配置,所以必须禁用。否则会一直报错,反复强调你的自定义头文件不可见。
-
VSCode 工作区 (.vscode 文件夹):存放上面提到的那些
settings.json,tasks.json等文件的地方,让项目的配置与 VSCode 编辑器关联起来。讲的直接一点,就是你现在正在编译的那个项目的文件夹,比如我写了一个平衡车程序,这个项目放在一个文件夹中,这个文件夹就是这个项目的根目录,这里提到的工作区,在我对平衡车项目进行编辑时,指的就是存放平衡车项目的文件夹。 -
IntelliSense:VSCode 的“智能感知”功能的代号,即代码自动补全、参数提示、跳转定义等功能的统称。
1、项目结构
(1).c源文件文件夹
(2)头文件文件夹
(3)执行文件文件夹,就是那个build文件夹,可有可无。里边打算放exe文件,当然我这里没体现。
(4).vscode文件夹 这个里边的文件主要是给编译器用的,约定一些编程过程中的事情。
(5)Makefile文件
2、建立过程
(1)建立Makefile文件
Makefile文件文件是没有格式的,这个文件的用途是当项目文件较多的时候,直接写gcc文件不方便,所以采用makefile文件进行管理。
这种文件的核心思想是按规则自动化构建:只有当源文件发生改变时,才会重新编译相关部分,这里是一个makefile文件的例子。
CC = gcc # 指定编译器和头文件路径
CFLAGS = -Wall -Wextra -I./inc
SRCS = $(wildcard src/*.c) # 自动找出 src 目录下所有的 .c 文件
OBJS = $(SRCS:.c=.o) # 将 .c 文件名替换为 .o 目标文件
TARGET = main # 最终生成的可执行文件名
$(TARGET): $(OBJS) # 链接规则:把所有的 .o 合成一个可执行文件
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c # 编译规则:把每个 .c 单独编译成 .o
$(CC) $(CFLAGS) -c $< -o $@
clean: # 清理规则:删除编译生成的垃圾文件
rm -f $(OBJS) $(TARGET) main.exe
这里对这个文件内的含义进行解释:
# 做法和菜谱——指定编译器和头文件路径
CC = gcc #
CFLAGS = -Wall -Wextra -I./inc
CC:定义编译器变量,后续用 $(CC) 时就会被替换成 gcc。
CFLAGS:编译器参数变量:
-Wall:开启几乎所有常见的警告。
-Wextra:在 -Wall 基础上再额外开启一些警告。
-I./inc:告诉 gcc 去 ./inc 目录里找头文件(你的 my.h 就在里面)。
#备菜阶段——源文件的提取与处理
SRCS = $(wildcard src/*.c) # 自动找出 src 目录下所有的 .c 文件
OBJS = $(SRCS:.c=.o) # 将 .c 文件名替换为 .o 目标文件
TARGET = main # 最终生成的可执行文件名
其中:
wildcard src/*.c: Make 的一个函数,用来获取所有匹配 src/*.c 模式的文件列表。例如 src/main.c src/math.c
SRCS: 保存这个源文件列表。
$(SRCS:.c=.o): 一个替换引用,把列表里每一个 .c 的后缀换成 .o, 比如 src/main.o src/math.o。
OBJS: 保存目标文件列表。
TARGET: 最终生成的可执行文件名,这里是 main,可以这样理解计算机认可的可执行文件名就是TARGET,这个类似于变量,相当于将main这个“名字”放在这个变量中,这样可执行文件名就叫做main.exe。当然,要是给它改成m111,那么就会生成m111.exe文件。
# 链接规则:把所有的 .o 合成一个可执行文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
# 编译规则:把每个 .c 单独编译成 .o
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理规则:删除编译生成的垃圾文件
clean:
rm -f $(OBJS) $(TARGET) main.exe
rm -f指的是删除
注意,这里的makefile文件一定是在项目根目录中,也就是说一定要确认makefile文件和inc、src等文件夹是同一个级别。
这个文件的功能就是使得使用者不再需要通过输入比较长的shell(shell可以理解问命令行)来编译项目。
注意!这里一直在编译工程,还没有运行工程。
(2)建立.vscode文件夹
在这个文件夹中的文件均为.json类型,这个文件夹用来存放工作区专属配置。
1)tasks.json 文件
这个文件的功能是告知VScode如何执行命令行任务,这里所谓的任务指的是诸如编译,执行等各种功能。对初学者而言这样说非常笼统,因为就像一个第一次开店不知道该定哪些章程的人一样,编程者这个时候是手足无措的,根本不知道命令执行过程中都有哪些需求,哪些方面是需要被控制。
现在,以C语言编程中最常见的指令“编译指令make”为例子,叙述一下在这个任务执行过程中tasks干了什么,是如何对这个任务的执行方法进行规定的以及规定的目的是什么:
首先在编译过程中,tasks的作用就是帮助使用者自动的在终端执行了mingw32-make命令,而不用使用者手动将这个命令敲在命令行中。
接下来看一看在tasks中如何实现这个目标
{
"version": "2.0.0", // 任务配置的格式版本,不动它
"tasks": [ // 可以定义多个任务,这是一个数组,注意一下,这个数组还挺长
{ //第一层花括号
"label": "build all C files", // 任务名称,显示在命令面板里
"type": "shell", // 任务类型:"shell" 表示执行命令行
"command": "mingw32-make", // 实际要执行的程序
"args": [ // 传给命令的参数
],
"options": {
"cwd": "${workspaceFolder}",
}
},
"group": {
"kind": "build", // 把此任务归类为“构建任务”
"isDefault": true // ***设为默认构建任务,Ctrl+Shift+B 直接触发
},
"problemMatcher": "$gcc" // 让 VSCode 能输出解析错误信息,点击可跳转,但注意不是这一句检查错误,检查错误的是makefile文件中的gcc
}
]
}
在这个文档中做的事情本质上就是在 “命令行” 和 “CTRL+SHIFT+B” 之间搭建了一个映射,另外又做了一些规定,包括执行这个指令时shell中要进行的文字反馈是什么样子
具体可以这样理解——
- 先定义一个任务名称,这里是build all c files,这个名称可以在tasks:Run Task中显示。
- 接着规定这是一个命令行任务(shell)。
- 接着说明具体的命令行,也就是说这个叫做build all c files的任务执行的是哪个命令行,这里执行的是mingw32-make。
- 把这个命令行命令需要的参数定义一下,在这里的参数(其实就是执行过程中的各个参与方)。
- 设置将当前文件所在的根目录设置为编译器编译这个文件时的根目录,要注意这里的根目录中有前面提到的makefile文件,make这个命令行在根目录中执行时就会去找makefile文件中的规则来执行。
- 给这个任务归个类,然后这一类任务就可以用同一组按键组合来激发。
- 设置在该任务执行时同事弹出错误提示。
这里配合上面提到的makefile文件,可以这样理解,就是设定一个叫做build all c file这样的一个任务,该任务执行mingw32-make指令,这个指令要在现在所在的项目根目录中执行。这个指令的具体动作是:找到根目录中的makefile文件,然后依次执行文件中的指令。
本质上就是一次热键自定义。
这里关于一些调用的问题我会在下一个文章中做个对比,进一步辨析,这里打个记号。实际上到这里为止问题解决差不多了。
2)建立launch.json文件
下面是这个文件的一个具体例子
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug C Program", //这个任务的名字叫做 Debug C Program
"type": "cppdbg", //调试器类型,cppdbg 对应微软 C/C++ 插件的调试器。
"request": "launch", //启动程序开始调试(而不是附加到已有进程)
"program": "${workspaceFolder}\\build\\main.exe", //和 tasks.json 输出一致
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [], // 额外环境变量
"externalConsole": false, //是否弹出一个独立控制台,就是dev中的黑框,不要的话就复用vscode提供的终端
"MIMode": "gdb", // 使用的调试器后端是 gdb
"miDebuggerPath": "gdb.exe", // 确保 gdb 在 PATH 中
"setupCommands": [ //调试器启动时执行的初始化命令列表,一个数组初始化了多条指令
{
"description": "Enable pretty-printing", //描述,给人看的
"text": "-enable-pretty-printing", //命令,给机器看的
"ignoreFailures": true //忽略失败
}
],
"preLaunchTask": "build all C files" //调试前先执行build all C files任务。
}
]
}
格式上来讲和上面的差不多,这段代码定义的是调试,这里要分辨一下调试和编译,前面的tasks中定义的就是编译,通过编译检查目标程序
3)建立c_cpp_properties.json文件
这个文件是为了更加漂亮的使用c插件,管写代码时的提示和报错。本质上有些锦上添花,但这种优化是必不可少的,文件内容如下:
{
"configurations": [
{
"name": "Multi_File_C", // 这个配置的名称,随便取
"includePath": [ // 告诉 IntelliSense 去哪里找头文件
"${workspaceFolder}/inc", // 你的头文件目录
"${workspaceFolder}/src" // 源文件目录(有时 .h 和 .c 放一起)
],
"defines": ["_DEBUG"], // 相当于在所有文件开头加了 #define _DEBUG
"compilerPath": "gcc", // 使用哪个编译器来分析代码(这里用 gcc)
"cStandard": "c99", // 使用 C99 标准
"intelliSenseMode": "windows-gcc-x64" // 智能感知模式,匹配 windows + gcc 64位
}
],
"version": 4
}
在这里有人可能会疑惑,明明前边通过调用gcc已经完成了检查和编译,这里又出现一个检查是不是多余,当然如果编程者能保证自己编程中不出现任何语法错误当然多余,但我们大多数人还是需要在写代码过程中出现的小的红色波浪线提醒一下自己还有一个花括号没打之类的事情,简而言之,这个文件是为了调用gcc的检查能力帮助编程者在编译前发现问题,它与gcc的区别在于,这里只管编译前的前端语法有没有毛病,不会真的试图生成编译后的文件,所以当面对一些文件配置问题,这里是不会显示的。
3、运行项目
(1)编译
首先要确定现在的位置是根目录才能编译,就是这里,cd是一个命令行,意思是切换。这里的my_first_Project文件夹就是这个项目的根目录,之前的.vscode文件夹和makefile文件就在这里。
直接按照tasks中定义的快捷键操作:shift+ctrl+b。然后就会出现文字说编译好了,
(2)运行
输入.\main.exe就可以运行,这里的main是我的文件名。有些麻烦是真的,可以再写一个.json文件约定一下使用快捷键运行的。
更多推荐

所有评论(0)