以下内容是根据本人自己的操作与思考写的,因此难免有纰漏与不严谨的地方,不足之处还请指出
望大家多多包涵
系统环境
版本 Windows 10 专业版
版本号 21H2
操作系统内部版本 19044.1766
VScode中C++环境配置
VScode与MinGW的下载
VScode下载(https://code.visualstudio.com)
Vscode的下载与安装比较简单,不赘述
MinGW下载(https://sourceforge.net/projects/mingw-w64/files/)
VScode就是一个编辑器而已,它并不是IDE,没有编译功能。因此需要我们自己下载编译器。这里使用MinGW。
点进去后下拉找到x86_64-posix-sjlj这个版本然后下载,我刚开始是随便找了个包就下了, 但是后面在编译gtest的时候报了多线程的错误,换了这个版本后问题解决了
x86_64是指64位的操作系统,i686是指32位的操作系统;
win32是开发windows系统程序的协议,posix是其他系统的协议(例如Linux、Unix、Mac OS);
异常处理模型 seh(新的,仅支持64位系统),sjlj(稳定的,64位和32位都支持),dwarf(优于sjlj的,仅支持32位系统)
(这段内容是我看了一些文章后偷的)
这篇文章写的比较详细
MingGW 各版本区别及安装说明(https://blog.csdn.net/qq_29856169/article/details/119380663)
如果使用浏览器直接下载速度慢的话,可以复制下载链接在迅雷中下载,亲测速度快不少。
我也有看到其它例如从sourceforge镜像站下载等的加速教程,感觉不如迅雷。。。效果
下载好了之后把压缩包解压到你希望放到的目标文件夹中,
路径不要有中文
(我道听途说的,没试过也不清楚有中文到底会不会出问题,但没中文肯定不会出问题)
配置环境变量
先简单说明一下配置环境变量的目的,就是为了能够让系统在任意目录下都可以直接输入程序名执行加入到系统环境变量中的目录下的程序,而不用加上这个程序所在的目录前缀
(如果遇到同名程序都配了环境变量好像是会执行环境变量写在前面的那个,不是很确定,有兴趣的朋友可以去求证一下🤔我记得我重装MinGW的时候没删之前的包和环境变量,然后给我折磨了好久)
解压完成后,应该是这样的
在这里面打开bin文件夹,能够看到里面有很多可执行文件,比如gcc.exe,g++.exe,gdb.exe等等,这就是要添加到环境变量中的路径了
右击此电脑,选择属性,右上角有个高级系统设置,打开后选中高级-环境变量-在系统变量中找到Path,双击打开或者点编辑打开,新建-把你自己的上面说的那个bin文件夹的完整路径复制后粘贴进去,确定后就配置完成了(用户变量与系统变量的区别这里不赘述了,
但如果你只用当前用户来做以下操作的话,配到用户变量的Path里应该也可以
没试过,😉你可以试试
)
右击win键,打开powershell
打开后输入gcc -v,有以下输出代表配置成功,最下面一行还会输出当前的版本
如果报错,说明没有配置成功,可以考虑多配置几遍,或者换一篇看
VScode中插件下载
打开VScode中的拓展,搜索并下载C/C++,CMake,CMake Tools这三个插件。如果你想让VScode界面显示中文,可以下载Chinese插件,其中CMake,CMake Tools这两个插件用于CMake
在VScode中编译运行C++
编译运行一个C++程序(单文件)
用VScode打开一个新建的文件夹,新建一个名叫Hello.cpp文件
输入以下代码:
保存后现在是不可以直接F5或者在上方点击运行的,这是由于.vscode文件夹中的文件还未配置。甚至现在还没有.vscode文件夹。关于这个.vscode文件夹,下文再说
那我们现在要如何编译运行这个cpp文件呢,我们首先从上方任务栏打开一个新的终端
在终端中输入
g++ Hello.cpp -o Hello.exe
,之后我们便能看到生成了一个名为Hello.exe的可执行文件
之后再在终端输入
./Hello.exe
或者
./Hello
就可以在终端中看到运行结果了
其实这个终端就是前文提到的powershell,所以直接用powershell做一样的事情也是可以的
简单解释一下这个流程,就是用g++.exe编译了Hello.cpp这个文件,-o Hello.exe的意思是将编译结果指定为名为Hello.exe的文件并输出到当前文件夹。如果在-o 后面带上完整的路径,就可以输出到指定的文件夹中。例如
g++ Hello.cpp -o ../../xxx/name.exe
等,其中./
与../
为相对路径,分别代表当前文件夹与父文件夹。 使用相对路径前需要明确工作路径 ,终端中可以直接看到当前工作路径是什么,后续可能会在CMakeLists.txt中使用相对路径,则需要确认工作路径后再使用相对路径
后面的./Hello.exe
也很好理解了,就是运行当前文件夹中的Hello.exe文件,其中.exe
后缀可以缺省
多文件编译
一般而言,不会将所有的功能实现全部放在包含main函数的单个文件中,有多个源文件或是需要链接库文件时,此时便需要多文件编译。同时要包含头文件的路径,当头文件与引用该头文件的源文件路径相同时可以缺省
多文件编译例子:
假设文件目录结构如下
其中mylib.h为头文件,mylib.cpp对头文件中声明的函数进行实现,最后在main.cpp文件中调用,main函数位于main.cpp文件中
mylib.h
mylib.cpp
main.cpp
假设当前工作路径为main.cpp文件所在路径
在终端中输入如下命令
g++ main.cpp ../funcs/mylib.cpp -I ../../include -o ../../bin/hellomylib.exe
,会发现根目录的bin文件夹下生成了hellomylib.exe文件,运行后发现结果符合预期。其中
-I
参数后接包含头文件的目录
静态库
,
动态库
也都可以通过上述方式链接。当然也可以通过
-l
与
-L
参数进行链接
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
所谓静态、动态是指链接方式不同
静态库在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中,其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件(.a文件)。生成用到
gcc -c
或g++ -c
命令,打包用到ar
命令
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。生成用到
gcc -fPIC -shared
或g++ -fPIC -shared
命令。动态库链接完成后,动态库文件需要放在链接时指定的目录,或是链接完成后生成的可执行文件目录,否则程序无法正确运行
静态库与动态库的生成与链接也可以看一下这篇文章,写的挺详细的
C++静态库与动态库
(https://www.cnblogs.com/skynet/p/3372855.html)
GDB Debug
GDB Debug
如果想要使用GDB对二进制可执行文件进行调试,那么在编译时需要加入参数
-g
,例:
g++ -g xxx.cpp -o xxx
。网上关于GDB调试的教程有许多,大家可以自己尝试。在这里提到GDB的主要原因是,配置完.vscode文件夹中的json文件后,可以启用GDB调试,并且在VScode中操作
.vscode中json文件的配置
.vscode中json文件的配置
上面的提到的单文件或多文件编译运行其实和VScode没什么关系,你可以在Powershell中做同样的事情,为了使VScode也可以编译运行调试C++程序,便需要配置.vscode文件夹中的json文件了。
先在这里放上官方文档吧
Using GCC with MinGW(https://code.visualstudio.com/docs/cpp/config-mingw)
简单总结一下,需要配置的json文件有3个,分别是:
-
tasks.json
(构建说明) -
launch.json
(调试器设置) -
c_cpp_properties.json
(编译器路径和智能感知设置)
其中需要重点配置的文件是前两个。如果你新建的项目没有.vscode文件夹和里面的json文件,只需要点击右上角的小齿轮,之后选择
g++.exe 生成和调试活动文件
,就会看到生成了.vscode文件夹,里面有launch.json和tasks.json两个文件。c_cpp_properties.json文件可以通过点击上方任务栏的"查看-命令面板"或者快捷键Ctrl+Shift+P打开,选择
C/C++:编辑配置(JSON)
生成
其实你可以直接创建一个名为.vscode的文件夹,然后再在里面创建这三个json文件,文件名别打错了就行
配置这些json文件时,会用到
预定义变量
,具体的解释可以参考官方文档
官方文档中的一些预定义变量(https://code.visualstudio.com/docs/editor/variables-reference)
tasks.json
先贴官方文档:https://code.visualstudio.com/docs/editor/tasks
tasks.json文件内容是构建说明,当
type
为
shell
时其实可以简单将它想象成在终端中输入指令
(拙劣的揣测,并不严谨)
来给大家看看我的tasks.json文件吧,用的还是上面的多文件编译的例子
简单说说几个我觉得重要的点:
-
type:任务的类型
-
label:任务的名称
-
command: 当type为shell时,就是shell命令,例如gcc,g++等
-
args:参数,每个参数都需要
"",
隔开," "
之间的空格字符不算参数的隔断 -
cwd:工作目录,要正确指定工作目录
-
dpendsOn:通过这种方式可以使一个任务在当前任务之前被执行,根据label来定位任务,我在这里使用是为了获取预定义变量的具体值,用以定位正确的工作目录
launch.json
launch.json是调试器设置,点击VScode上方任务栏的“运行-启动调试”或是按
F5
启动的就是调试器
这是我的launch.json文件
简单说说几个我觉得重要的点:
-
program:填你要调试的程序的路径,如果是通过tasks.json生成的话,就指定到生成目录的程序名
-
miDebuggerPath:填你自己的gdb路径,在mingw64的bin文件夹中,如果使用
\
做目录分隔符需要再写一个\
作为转义符,使用/
则不需要 -
preLaunchTask:这是在启动调试器前执行的tasks.json中的任务,通过label定位,所以tasks.json中你想执行的task的label需要和此处一致,调试前不需要执行任务可以不写
在launch.json文件中,可以直接点右下角的添加配置,选择c/c++: (gdb)启动,会自动帮你把很多内容补全,只需要修改少数内容即可实现功能
c_cpp_properties.json
官方文档https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference
最后是c_cpp_properties.json的配置,它的主要功能是配置编辑器的智能感知
这个文件其实不怎么需要配置,直接生成的就可以用了。可能不生成不配置也没什么影响
需要注意的点应该是compilerPath处需要填你自己的正在使用的编译器的完整路径,不然可能会影响编辑器智能感知
这三个json文件配置完成后,应该就可以在VScode中编译运行调试你的程序了。不行的话就多配置几遍或者看看别的文章吧。建议直接看官方文档
VScode中CMake的使用
VScode中CMake的使用
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。 ---------百度百科
CMake的下载与安装
CMake的下载与安装
下载地址https://cmake.org/download/
根据自己的系统版本进行下载即可
我看它从官网下载的下载链接是连的github的,所以下的慢的话有几种解决办法
-
UU加速器搜索学术资源进行加速,目前免费
-
github镜像站,把前面的github.com替换一下再下载,没试过效果,我找到的目前能用的几个
https://hub.xn–gzu630h.xn–kpry57d/
https://hub.nuaa.cf/
https://hub.fastgit.xyz/
https://hub.fastgit.xyz/
保不准什么时候就寄了,那就大家自己想想办法吧😜 -
可以从这个网站找合适版本下载https://cmake.org/files/LatestRelease/,直接用浏览器下的话可能也会很慢,但复制下载链接用迅雷下很快。 (上面那个下载地址好像不能直接复制下载链接用迅雷下,但这个可以。)
安装比较简单,安装程序可以勾选自动配环境变量的选项,没勾选的话记得配环境变量,配置方法与MinGW是一样的,网上教程也很多,不再赘述
CMake流程
CMake流程
首先介绍一下Makefile
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,也可以执行操作系统的命令。----百度百科
我的理解就是把类似于
gcc/g++ [-g] xxx.c(pp) [xxx1] [xxx2] [...] [-I] [xxx..] [-L] [xxx...] [-lxxx]...[-o] [xxx]
的流程以一种标准通用的格式写出来
有了Makefile后,使用
make
命令就可以自动编译链接源文件了
CMake是用于生成Makefile的,生成规则写在CMakeLists.txt文件中,通过
cmake
命令生成Makefile,再通过Makefile编译项目
CMake的构建有两种方式,
外部构建
与
内部构建
,其实区别就是是否新建一个文件夹然后再在这个文件夹的路径下进行
cmake
,我一般会使用外部构建,也就是新建一个文件夹再
cmake
CMakeLists.txt的编写
CMakeLists.txt的编写
先贴上官方文档吧,基本可以解答关于CMake的所有问题 CMake Reference Documentation (https://cmake.org/cmake/help/latest/)
一些其它的说明可以看看wiki是怎么写的:http://wiki.ros.org/catkin/CMakeLists.txt,或是去搜索相关资料,网上的内容还是非常丰富的
CMake是根据CMakeLists.txt来生成Makefile的,CMakeLists.txt的写法相较于直接写Makefile会更简单,同时也可以实现跨平台
关于CMakeLists.txt中的一些命令,先贴官方文档cmake-commands(7)(https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands)
CMakeLists.txt中,命令名字是不区分大小写的,而参数和变量是大小写相关的
介绍几个本文接下来例子可能会用到的的命令(个人理解,不严谨,详细释义与格式建议看官方文档)
-
add_executable 用于生成可执行文件
-
add_library 添加库文件
-
add_subdirectory 先执行子文件夹中的CMakeLists.txt后再返回执行当前CMakeLists.txt
-
aux_source_directory 找到一个目录中的所有源文件
-
include_directories 简单来说就是添加一个头文件搜索路径
-
target_link_directories 指定链接器在链接给定目标时应在其中搜索库的路径
-
target_link_libraries 链接库
-
set 给某个变量设置某个值(set的用法很多,建议查看官方文档)
-
find_library 找到某个库,并为其创建普通变量或缓存条目
-
message 在日志中记录指定的消息文本,向外输出消息
-
project 设置项目的名称
-
cmake_minimum_required CMake需要的最小版本
还有一些CMake中的变量,类似于VScode中的预定义变量,cmake-variables(7) (https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html)
一个小tips,我好像没有找到在CMake中直接获取父文件夹路径的方式,相对路径只能用于某些命令中,例如message就无法通过使用转义符输出相对路径。
我搜索了一些相关的内容,找到了方法。就是通过正则表达式来获取父路径。
CMake中可以使用string
命令来进行正则匹配string(REGEX REPLACE <match-regex> <replace-expr> <out-var> <input>...)
,详细请看官方文档
因此可以通过正则表达式的分组,以/
为分隔将当前项目路径进行分组,取前面的分组内容即可得到父路径,父父路径等
例如:(这段我自己写了试了应该是没什么问题的,在当前CMakeLists.txt有project
时)(后面的变量也可以换成其它CMAKE路径变量或是自定义的路径)(\\
是转义符+\
)
string(REGEX REPLACE "(.*)/(.*)/(.*)" "\\1" GRANDFATHER_DIR ${PROJECT_SOURCE_DIR})
string(REGEX REPLACE "(.*)/(.*)/(.*)" "\\2" FATHER_NAME ${PROJECT_SOURCE_DIR})
set(FATHER_DIR "${GRANDFATHER_DIR}/${FATHER_NAME}")
message(${FATHER_DIR})
message(${GRANDFATHER_DIR})
message(${FATHER_NAME})
正则我是真的看的头大,这个算比较简单的运用
正则表达式(https://www.runoob.com/regexp/regexp-syntax.html)
大家可以看看官方文档中的内容,去查找自己想要的变量,或者去网上搜对应的内容,遇到不清楚的内容记得活用搜索功能,以官方文档为准
还是拿上面的多文件编译例子来进行一次完整的CMake流程吧
我们先在根目录下新建一个build文件夹,再在根目录、src/funcs与src/main中新建CMakeLists.txt文件,如下图所示
根目录下的CMakeLists.txt
src/funcs下的CMakeLists.txt
src/main下的CMakeLists.txt
CMakeLists.txt编写完成后,只需要先
cmake
生成Makefile文件,再
make
完成整个编译流程即可
在Windows中,
cmake
命令会优先选择msvc编译器,因此需要指定编译器为MinGW,需要添加参数
-G "MinGW Makefiles"
。
make
指令也需要更改为
mingw32-make
因此完整的流程就是先进入build文件夹,
cmake .. -G "MinGW Makefiles"
,
mingw32-make
结果如下
.vscode中json文件的配置
.vscode中json文件的配置
前面已经讲过.vscode中的json文件该如何配置了,所以我就简单把tasks.json与launch.json文件贴出来好了。还不太清楚的话可以多看几遍前面的内容
tasks.json
launch.json
之后就可以在VScode中进行CMake并调试运行了
Gtest
Gtest
Google C++单元测试框架(简称Gtest),可在多个平台上使用(包括Linux, Mac OS X, Windows, Cygwin和Symbian),它提供了丰富的断言、致命和非致命失败判断,能进行值参数化测试、类型参数化测试、“死亡测试”。
关于Gtest如何使用的文章网上挺多的,我推荐两篇
GTest 总结(https://blog.csdn.net/qq_36631379/article/details/119387921?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-119387921-blog-105031271.pc_relevant_aa2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-119387921-blog-105031271.pc_relevant_aa2&utm_relevant_index=1)
GoogleTest单元测试(https://www.cnblogs.com/jycboy/category/900460.html)
Gtest的下载
Gtest的下载
https://github.com/google/googletest
国内下的慢或者打不开的话可以用uu加速器,github镜像站等等方法,一些目前可用的镜像站在上面cmake下载那里贴出来了
下载zip,之后解压到你的项目文件夹中即可
Gtest的简易使用
Gtest的简易使用
解压完成后可以看到这个文件夹中是有CMakeLists.txt的,因此只需要在这个googletest-main的文件夹中新建一个build文件夹,之后在这个build文件夹中cmake即可
在终端输入
cmake .. -G "MinGW Makefiles"
,
mingw32-make
后,生成了四个库文件,其中在接下来的例子中我们需要用到的为libgtest.a,可能会用到libgtest_main.a。是否用到libgtest_main.a取决于有没有自己写main函数
下面是gtest使用的简单例子,要测试的函数是返回两个整型数中的最小值
代码如下
mylib.h
mylib.cpp
main.cpp
我们先在src目录中新建一个tests文件夹,在tests文件夹中新建test1.cpp文件,在其中写测试用例
test1.cpp
为了避免链接时要输入一大串的文件路径,我们将libgtest.a复制到根目录下的lib文件夹中,没有lib文件夹可以新建一个,gtest.h头文件是放在googletest-main/googletest/include/gtest中的,我们直接把整个gtest文件夹复制到根目录的include中,再进行编译链接,由于头文件的写法是
"gtest/gtest.h"
,所以
-I
后的参数为根目录下的include目录
上述操作其实都可以不做,能够保证链接时路径的正确即可
目录结构
在终端中输入
g++ main.cpp ../funcs/mylib.cpp ../tests/test1.cpp ../../lib/libgtest.a -I ../../include -o gtest01.exe
运行getst01.exe,可以看到如下结果
测试结果会打印出来,告诉你哪些成功了,哪些失败了,这样子就算是简单的使用gtest进行了测试
如果没有写main函数,只写了待测函数,链接时将待测函数源码与libgtest.a还有libgtest_main.a进行链接即可
例如:
g++ ../funcs/mylib.cpp ../tests/test1.cpp ../../lib/libgtest.a ../../lib/libgtest_main.a -I ../../include -o gtest01.exe
Gtest+CMake
Gtest+CMake
两种方法,第一种是直接找到libgtest.a这个库,include头文件路径,直接链接。第二种是用
add_subdirectory
将googletest-main文件夹下的CMakeLists.txt加入到根目录下的CMakeLists.txt中,链接时用
target_link_libraries(XXX gtest)
即可
第一种在src/main中的CMakeLists.txt中修改
第二种
根目录下的CMakeLists.txt
src/main中的CMakeLists.txt
用第二种方法可以不用先在googletest-main文件夹中进行cmake
cmake完之后运行就可以看到结果啦
以上就是本篇的全部内容了,如有不足或错漏还请指出