
vsvode是一款非常灵活的编译环境其依靠c语言插件进行C语言编程由于没有版权费用现在越发普及这里是描述归纳了我在学习在vscode中建立多文件的c项目过程中的一些坑和总结。欢迎交流。0、名词解释在这里我将做一个索引主要用来解释在下文中可能会遇到的专业名词。1核心工具链类这些东西主要用来编译可以将他们理解为一个翻译器目的是将C语言源文件翻译为电脑能够识别的二进制文件。gcc这个是编译器make / mingw32-make: 这个是编译器的编译指令gdb: 这个是调试工具在vscode中可以通过按F5让程序暂停单步执行2关键配置文件Makefile构建规则脚本。告诉make源文件在哪、头文件在哪、怎么编译、怎么链接。tasks.jsonVSCode 的任务配置。在 VSCode 里定义“任务”比如调用gcc或make。launch.jsonVSCode 的调试配置。告诉 VSCode “用哪个调试器gdb去调试哪个程序build/main.exe”通常配合F5使用。c_cpp_properties.json智能感知配置。专门给编辑器IntelliSense看的让它知道去哪里找头文件代码补全和“跳转到定义”才能正常工作3一些概念Code Runner一个 VSCode 扩展让你一键运行单文件代码。但它是多文件项目的“杀手”因为它只会编译当前文件不理会你的头文件配置所以必须禁用。否则会一直报错反复强调你的自定义头文件不可见。VSCode 工作区 (.vscode 文件夹)存放上面提到的那些settings.json,tasks.json等文件的地方让项目的配置与 VSCode 编辑器关联起来。讲的直接一点就是你现在正在编译的那个项目的文件夹比如我写了一个平衡车程序这个项目放在一个文件夹中这个文件夹就是这个项目的根目录这里提到的工作区在我对平衡车项目进行编辑时指的就是存放平衡车项目的文件夹。IntelliSenseVSCode 的“智能感知”功能的代号即代码自动补全、参数提示、跳转定义等功能的统称。1、项目结构1.c源文件文件夹2头文件文件夹3执行文件文件夹就是那个build文件夹可有可无。里边打算放exe文件当然我这里没体现。4.vscode文件夹 这个里边的文件主要是给编译器用的约定一些编程过程中的事情。5Makefile文件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./incCC定义编译器变量后续用 $(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.cSRCS 保存这个源文件列表。$(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.exerm -f指的是删除注意这里的makefile文件一定是在项目根目录中也就是说一定要确认makefile文件和inc、src等文件夹是同一个级别。这个文件的功能就是使得使用者不再需要通过输入比较长的shellshell可以理解问命令行来编译项目。注意这里一直在编译工程,还没有运行工程。2建立.vscode文件夹在这个文件夹中的文件均为.json类型这个文件夹用来存放工作区专属配置。1tasks.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 // ***设为默认构建任务CtrlShiftB 直接触发 }, problemMatcher: $gcc // 让 VSCode 能输出解析错误信息点击可跳转但注意不是这一句检查错误检查错误的是makefile文件中的gcc } ] }在这个文档中做的事情本质上就是在 “命令行” 和 “CTRLSHIFTB” 之间搭建了一个映射另外又做了一些规定包括执行这个指令时shell中要进行的文字反馈是什么样子具体可以这样理解——先定义一个任务名称这里是build all c files这个名称可以在tasksRun 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中定义的快捷键操作shiftctrlb。然后就会出现文字说编译好了2运行输入.\main.exe就可以运行这里的main是我的文件名。有些麻烦是真的可以再写一个.json文件约定一下使用快捷键运行的。