本文概述
本文记录了笔者在使用vsc踩过的无数的坑和一些觉得有必要提醒一下初学者的知识。希望可以为广大想入坑vsc的同志一些帮助。
但请注意,本文并非是一篇手把手教你怎么配置的教程(tutorial),而是一篇指导性的、注重授人以渔的教学。也就是说,我不会提供现成的、可供复制的json文件。我更希望你可以从本文学到知识,然后可以自己去配置个人风格的环境。
本篇使用的编译器是MinGW,更多参考可以去翻阅Microsoft提供的官方教程文档,个人感觉官方文档比许多个人的教程都要适合萌新。因为很多个人发布的教程只是为你提供了一份带有个人色彩的答案,并不能因地制宜地适用于所有人的情况。笔者就是翻阅无数教程,结果这有问题那有问题,最后还是要自己去思考、解决。
链接:
Microsoft帮助文档
关于VScode
首先要明确一点:
vscode只是一个编辑器。
编辑器就好比word和Windows记事本,只是一个可以让你编辑文本、保存文件的工具。当你写好了一个HelloWorld.c的时候,你希望vscode像你之前用过的IDE一样为你生成exe文件,然后在控制台打出Hello world。但是事实是vsc也不知道该如何去做这件事。而Visual Studio、CodeBlocks、甚至是有点简陋的DevC++和VC++6.0都属于已经被别人配置好的集成开发环境(Integrited Develop Environment,也就是IDE),它们是知道如何去做这些事的。所以你不能用对待IDE的逻辑去对待VScode,其他编辑器比如vim也是同理。
悲剧的是很多萌新,甚至包括很多已经修完一门语言课程的大学生自己也不知道如何去编译、运行自己的代码。只会去复制一个别人的json文件然后出现各种各样的错误。最后放弃vscode回到ide的温室内,错失了一款好用的编辑器。
我会在下文演示如何使用gcc编译器,以及用它在vsc里面搭建环境。学会了之后,可以自己去改造vsc默认生成的json模板或者解决一些问不到答案的问题。
GCC的使用
基本编译操作
相信看这篇文章的你已经为配置vsc努力过了,不用我再演示如何下载、安装MinGW。如果没有,可以去参阅帮助文档或者其他人制作的更详细的教程。
gcc是一款GNU推出的,可以编译C,C++,Objective-C的编译器。在Windows上,它与一众其他软件(调试器gdb,C++编译器g++等)一并打包在MinGW中。如果你不是用着一部远古电脑,你应该选择MinGW64位版本。
现在我写了一个main.cpp
文件,还是我们编程界的优良传统HelloWorld,非常的简单,你也可以用C或者OC写出来:
//你可以使用记事本写出来,不过注意文件尾名。
#include<iostream>
#include<string>
using std::cout;
using std::string;
int main(void)
{
string s{"Hello World!"};
cout << s <<std::endl;
return 0;
}
我把它保存在了D:\pspro\C_C++_pro\Project\HelloWorld\src
这个文件夹里面,你找一个你喜欢的地方放就好了。
按下键盘上的win+R键,输入cmd,这个黑屏窗口就是你的操作系统,只不过后来我们都习惯了图形界面(GUI)了。
当然,如果你已经有vscode,也可以直接使用他的内置终端(Integrited terminal)。
在桌面使用Shift+右键进入power shell也是可以的。和cmd差别不大。
这时候你可以检查一下gcc是否安装成功了,直接输入命令gcc就行了。
输入gcc, gcc --version都是可以的,上图里面我还演示了Python。
如果cmd表示并不知道gcc是啥,那说明你需要去改一下环境变量,告诉cmd,gcc到底是什么东西,这个东西又到底在哪。如果不知道怎么修改环境变量,你可以查阅帮助文档或者其他人的教程。
PS: 该方法在你配置Python、Java环境的时候也会用到。
输入命令cd + [文件夹路径],可以抵达你想去的工作区域。比如我现在进入了我存放main.cpp
的src文件夹。
到这里,你已经可以操作src下的文件了。接下来要做的事情,就是告诉gcc你的想编译这个main.cpp
文件。我们可以用:
gcc main.cpp
g++ main.cpp
后者是因为我使用C++文件对应的g++,C语言选手使用gcc即可。
在这种情况下,生成的exe会一般默认命名为a.exe
,存放在你打开的这个文件夹里面。
此时直接输入a,cmd就会为你运行a.exe
,输出hello world。
小贴士:如果你不想看见之前cmd打出来的各种你失败的证据,可以输入cls清除。这样控制台就干净了。
cls
=Clean Screen
你在修改代码之后需要重新编译才能运行新的程序,不过gcc会自动帮你覆盖掉之前旧的exe,无需手动删除。
更多编译操作
我们现在已经熟悉了一下基本的命令行操作,按照如下格式:
${workspaceFolder}>[命令] [命令行参数...]
刚才我们的命令都是gcc或者g++,命令行参数只有一个main.cpp,表示要编译的文件。现在我们介绍更多命令行参数。
gcc -o [输出文件名字] main.cpp
//-o参数可以让你为编译输出文件命名。
gcc -c [name] main.cpp
//-c可以编译出obj文件,在链接库的时候会用到。
gcc -o [文件夹路径1]/[name] [文件夹路径2]/main.cpp
//把文件夹2里面的main.cpp编译之后放到文件夹1里面去,并且命名为[name].exe。
不同的命令行参数可以对gcc的操作作不同的指导,比如生成汇编语言文件,我在这里不会多讲,有兴趣的可以自己去查阅。
多个源文件的编译操作
同上一节差不多,不过我们会用到更多。有人可能会推荐你使用CMake
进行多个文件操作,但CMake更多用于大工程,而且。。我也还没学会。
有耐心的不妨点个关注,也许哪天我就学会了,而且出了CMake的教程呢?()
我们还有头发的人,用的多个文件一般指的就是包含有好几个 .cpp 文件和几个 .h 文件。当然有人可能会用 .o 文件和 .a (静态库文件)联合编译。我们在这里需要的就是不一样的命令行参数:
命令行参数 | 意义 |
---|---|
-I(大写i)+ 路径 | 指定你自己写的头文件的路径 |
-g | 输出调试信息 |
-Wall | 开启额外的警告 |
-std | 指定语言标准,如-std=c++17,-std=c11等 |
-fexec-charset | 指定编码表,比如ASCII,UTF,Unicode,GBK,不兼容的编码表会导致乱码。 |
gcc的命令行参数有很多,有兴趣的可以自行查找。
VScode配置.json
刚刚我们学那么多关于gcc的命令,就是为了可以让我们现在无阻碍地解决vsc配置里面的核心问题:tasks.json
和launch.json
的配置。
按下Ctrl+K+O
打开一个你准备用来写代码的文件夹,我这里是illustration
。
此时你按下Ctrl+Shift+P
,向vscode输入命令task等等就会生成一系列.json
文件。如果你真的不清楚如何弄出这几个文件夹,那还是让别人教你吧,比如官方文档。
.vscode
储存了上图三个文件。这个文件夹非常重要,它会教vscode如何像IDE一样办事。
~
tasks.json
tasks.json
会指导vsc编译、运行你的文件。
vsc会为你默认生成一个模板,如果你安装了C/C++ Extension
,intellisense会为你解释每一行的意义。
看见上面的网址了吗,官方的教材文档在向你招手。
"command"
就是要输入的命令,默认是MSVC,我们正在使用gcc,所以改成gcc或者g++。
"command": "gcc",
//g++ 适合于C++。
"args"
是命令行参数,就是我刚才教你的啦~~
"args": [ //方括号里是传给gcc的参数
"${file}", //指定要编译的是当前文件
"-o", //指定输出文件的路径和名称
"${fileDirname}\\bin\\${fileBasenameNoExtension}.exe",//承接上一步的-o,让可执行文件输出到源码文件所在的文件夹下的bin文件夹内,并且让它的名字和源码文件相同
"-I",//头文件(自己写的)
"",//头文件所在文件夹
"-lfunc",//外部库的名字
"-L./",//外部库的路径
"-g", //生成和调试有关的信息
"-Wall", // 开启额外警告
"-static-libgcc", // 静态链接libgcc
"-fexec-charset=GBK", // 生成的程序使用GBK编码
"-std=c++17", // 语言标准]
至于${fileDirname}
这样子的东西,其实是一种代称,英语好的同学可以直接看出来它代表的意义。
表达式 | 意义 |
---|---|
${file} | 你窗口正在显示的这个文件 |
${fileDirname} | ${file} 所在的文件夹 |
${fileBasenameNoExtension} | 去掉文件尾名的文件名 |
${workspaceFolder} | 就是你最开始打开的文件夹,大写名字的那个 |
可以参考,但不要复制粘贴我的json文件,这样导致的问题我概不负责。
完整的tasks.json
:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Compile",//取个好听的名字,不过要记得,待会要用
"type": "shell",
"command": "g++",
"args": [
//This is GNU version
"-g",
//-o to name the outputfile.
"-o",
"${fileDirname}/../bin/${fileBasenameNoExtension}",
//The path of header file.
"-I",
"${fileDirname}/../include/",
//Path of src file
"${fileDirname}/../src/${fileBasenameNoExtension}.cpp",
"${fileDirname}/../lib/Maxim.cpp",
"-Wall"
],
"group": "build",
"presentation": {
// Reveal the output only if unrecognized errors occur.
"reveal": "silent"
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile"
},
{//这个大括号里是‘运行(run)’任务,一些设置与上面的构建任务性质相同
"label": "run",
"type": "shell",
"dependsOn": "build", //任务依赖,因为要运行必须先构建,所以执行这个任务前必须先执行build任务,
"command": "${fileDirname}\\..\\bin\\${fileBasenameNoExtension}.exe", //执行exe文件,只需要指定这个exe文件在哪里就好
"group": {
"kind": "test", //这一组是‘测试’组,将run任务放在test组里方便我们用快捷键执行
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": true, //这个就设置为true了,运行任务后将焦点聚集到终端,方便进行输入
"panel": "new"
}
}
]
}
附上我的文件组织结构,可以与上面的路径对照:
launch.json
launch.json
指导了运行和调试。
MinGW附上了调试器gdb
,用这个我们就可以逐步运行我们的程序并且监视运行的情况,从而找出bug。
PS:VScode的调试界面极其优雅。
{
"version": "0.2.0",
"configurations": [
{
"name": "GDB Debugger",//取个你爱听的名字
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}\\..\\bin\\${fileBasenameNoExtension}.exe",
//program是你要运行的程序,也就是exe
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "D:/pspro/mingw64/mingw64/bin/gdb.exe",//调试器所在地
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": false
}
],
"preLaunchTask":"Compile"//调试之前要做的事情,这里是tasks的编译。
//Comiple是我给tasks取的名字。
},
]
}
c_cpp_properties.json
c_cpp_properties.json
是关于语言版本等一些信息。
这个需要改的不多,可以参考我在下面写的注释
{
"configurations": [
{
"name": "Win32",
"includePath": [//工作区之外的头文件路径
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
""
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "D:\\pspro\\mingw64\\mingw64\\bin\\g++.exe",
//编译器所在地
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "gcc-x64"
//intellisense模式,这里是gcc编译器64位版本。
}
],
"version": 4
}
有了以上知识,你就可以自己写适合自己的json文件,并且写一些简单的程序。如果还有问题或者发现本文里面的错误,可以在下面评论或者私信我。
常见问题
这里是我碰见过的一些百思不得其解的问题。
1,找不到头文件
许多人会直接叫你去c_cpp_properties.json
里面添加路径。其实不能这样一概而论。
头文件分两类,一类是gcc会给你带来的头文件,比如stdio.h
,iostream.h
等等,这一类文件编译器自己知道在哪,一般在MinGW文件夹里面的include内如果编译器找不到的话,最好查阅MinGW文档或者其他人提供的解决方案,我并没有遇到过。
第二类是自己编写的或者外部引入的头文件,这类文件如果还在你的${workspaceFolder}
内的话就不需要修改c_cpp_properties.json
,因为默认生成的c_cpp_properties.json
里面有这样一段:
"includePath": [
"${workspaceFolder}/**"
],
"${workspaceFolder}/**"
代表在${workspaceFolder}
内递归查找,不管有多少子目录,每一个文件都一定会被搜索到。
这种情况下,检查tasks.json
内的:
"-I",
"${fileDirname}/../include/",
路径是否正确。
2,undefined reference to …
因为我们经常把声明写在头文件,定义写在库文件或者其他源文件内。这很有可能是相应的库文件或者写有定义的源文件未链接进来。解决思路是检查tasks.json
内的:
"${fileDirname}/../src/${fileBasenameNoExtension}.cpp",
"${fileDirname}/../lib/Maxim.cpp",
"${fileDirname}/../lib/..."
源文件是否齐全。
结语
这是本人的第二篇博客,笔者也只是接触编程还不足半年的编程新手,不敢保证以上内容没有错误,所以希望读这篇文章的人多思考、多查证。如果有疑问或者错误,可以留言评论区或者私信我,我会尽量回应。