基于VSCode和CMake进行C/C++开发
写在前面
本文为Linux下的C/C++开发笔记,参考b站视频BV1fy4y1b7TC。
Linux指令
man
和help
查看指南(man = manual)ls
和tree
查看文件
ls -lah # 显示文件
# -l 表示列表显示
# -a 表示全部显示(包括隐藏文件)
# -h 表示可读显示(h = human-readable)
tree # 树形显示
开发环境搭建
- 安装GCC、GDB(通常是自带的)
# 更新源
sudo apt update
# 通过以下命令安装
sudo apt install build-essential gdb
# 确认安装成功(-v = --version)
gcc -v
g++ -v
gdb -v
- 安装CMake
# 通过以下命令安装
sudo apt install cmake
# 确认安装成功(-v = --version)
cmake -v
GCC编译器
VSCode通过调用GCC编译器实现C/C++编译:
- gcc编译C
- g++编译C++
编译过程
- 预处理(Pre-Processing)
# -E 选项指示编译器仅对输入文件进行预处理,生成.i文件
g++ -E test.cpp -o test.i
- 编译(Compiling)
# -S 选项指示编译器为C++代码产生汇编语言文件后停止编译,生成.s文件
g++ -S test.i -o test.s
- 汇编(Assembling)
# -c 选项指示编译器把源代码编译成机器语言的目标代码,生成.o文件
g++ -c test.s -o test.o
- 链接(Linking)
# -o 选项指示编译器生成可执行的二进制文件
g++ test.o -o test
以上4个步骤可以一次性完成
# g++后面可以接多个.cpp文件
g++ test.cpp -o test
重要编译参数
-g
编译输出带调试信息的可执行文件
g++ -g test.cpp
-O[n]
优化源代码,n越大越快(n常为0-3)
# 使用-O2优化源代码(-O = -O1)
g++ -O2 test.cpp
# 量化运行时间
time ./test
-l
/-L
指定库文件/库文件路径
# -l紧接库名,-L紧接库路径
# 在/lib和/usr/lib和/usr/local/lib里的库可直接用-l链接
# 链接glog库
g++ -lglog test.cpp
# 如果库文件不在上面三个目录里,需要用-L链接
# 链接libtest库,在/home/user/libfolder目录下
g++ -L/home/user/libfolder -llibtest test.cpp
-I
指定头文件搜索目录(大写i)
# 如果头文件不在/usr/include目录里,需要用-I指定
# 如-I/home/user/include,当前目录用-I.指定
g++ -I/home/user/include test.cpp
-Wall
/-w
打印/关闭警告信息
# 打印警告
g++ -Wall test.cpp
# 关闭警告
g++ -w test.cpp
-std=c++11
设置编译标准
# 使用c++11标准编译
g++ -std=c++11 test .cpp
-o
指定输出文件名
# 指定输出文件名为test(不指定默认为a.out)
g++ test.cpp -o test
-D
定义宏
常用场景:
-DDEBUG
定义DEBUG宏,用-DDEBUG开启或关闭DEBUG
# include <stdio.h>
int main(){
#ifdef DEBUG
printf("DEBUG LOG\n");
#endif
printf("in\n");
}
# main.c中#ifdef DEBUG部分内容可被执行
gcc -DDEBUG main.c
编译和链接静态/共享库
假设目录结构如下:
include/Swap.h
main.cpp
src/Swap.cpp
- 直接链接
g++ main.cpp src/Swap.cpp -Iinclude
- 静态库
# 进入src
cd src
# 汇编,生成Swap.o (默认)
g++ Swap.cpp -c -I../include
# 生成静态库libSwap.a(ar = archive)
ar rs libSwap.a Swap.o # lib开头就行
cd ..
# 链接,生成可执行文件static
g++ main.cpp -Iinclude -Lsrc -lSwap -o static
- 共享库
# 进入src
cd src
# 生成共享库libSwap.so(PIC = position independent code)
g++ Swap.cpp -I../include -fPIC -shared -o libSwap.so
## 上面的命令等价于以下两条命令
# g++ Swap.cpp -I../include -c -fPIC
# g++ -shared -o libSwap.so Swap.o
cd ..
# 链接,生成可执行文件shared
g++ main.cpp -Iinclude -Lsrc -lSwap -o shared
注意:运行共享库链接的可执行文件与前两种不同,需要指定库路径
# 运行共享库链接的可执行文件
LD_LIBRARY_PATH=src ./shared
GDB调试器
- 编译时使用
-g
参数 - 调试时执行
gdb [可执行文件名]
- 回车键重复上一命令
$(gdb)run(r) # 重新开始运行文件
# run argv[1] argv[2] 调试时命令行传参
$(gdb)list(l) [行号] # 查看某行为中心的源代码
# list [函数名]查看函数
# list 查看当前行为中心的源代码,再次list查看后续代码
$(gdb)break(b) [行号] # 在某行加入断点
$(gdb)delete(d) [断点号]# 删除断点
$(gdb)continue(c) # 继续循环
$(gdb)print(p) [变量名] # 打印值及地址
$(gdb)display [变量名] # 追踪查看变量
$(gdb)undisplay [追踪号] # 追踪查看变量
$(gdb)watch [变量名] # 设置观察变量,发生修改时打印
$(gdb)info(i) breakpoints(b) # 查看断点
$(gdb)info(i) display # 查看追踪点
$(gdb)info(i) watch # 查看观察点
$(gdb)next(n) # 单步调试,逐过程
$(gdb)step(s) # 单步调试,逐语句
$(gdb)finish # 跳出当前函数
$(gdb)quit(q) # 退出调试
CMake
- 基本语法格式:指令(参数1 参数2)
- 变量使用
${}
方式取值,但IF语句中直接使用变量名
set(HELLO hello.cpp) # 设置变量
add_executable(hello main.cpp ${HELLO}) # 使用变量
# IF语句中使用 IF(HELLO)
- 指令大小写无关,参数和变量大小写相关
重要指令
假设目录结构如下:
include/libHelloSLAM.h
src/libHelloSLAM.cpp
useHello.cpp
cmake_minimum_required
声明要求的 cmake 最低版本
# 声明要求的 cmake 最低版本
cmake_minimum_required(VERSION 2.8)
project
声明一个 cmake 工程
# 声明一个 cmake 工程
# 语法:project(工程名 [支持的编程语言])
project(HelloSLAM)
set
定义变量
# 语法:set(变量名 值1 值2 ...)
# 设置编译模式(Debug相当于-g,Release相当于-O3)
set(CMAKE_BUILD_TYPE "Debug")
# 追加编译参数
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# 指定C++编译器
set(CMAKE_CXX_COMPILER "/usr/local/gcc/bin/g++")
# 可执行文件输出的存放路径(${PROJECT_SOURCE_DIR}为最近的CMakeLists.txt所在位置)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 库文件输出的存放路径(也可以用${CMAKE_SOURCE_DIR})
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
include_directories
添加头文件搜索路径(相当于g++的-I参数)
# 添加头文件搜索路径(相当于g++的-I参数)
include_directories(./include)
link_directories
添加库文件搜索路径(相当于g++的-L参数)
# 添加库文件搜索路径(相当于g++的-L参数)
link_directories(./lib) # 搜索额外的库文件
add_library
添加库
# 共享库 (不加SHARED是静态库)
add_library(hello_shared SHARED ./src/libHelloSLAM.cpp)
add_executable
添加可执行程序
# 添加可执行程序调用hello库中函数
# 语法:add_executable( 程序名 源代码文件 )
add_executable(useHello useHello.cpp)
target_link_libraries
将共享库文件链接到可执行程序上
# 将库文件链接到可执行程序上
target_link_libraries(useHello hello_shared)
编译指令
# 创建build文件夹
mkdir build
cd build
# 配置cmake(CMakeLists目录下)
cmake ..
# 编译
make
最后得到的目录结构
include/libHelloSLAM.h
src/libHelloSLAM.cpp
build/....(省略)
bin/useHello
lib/libhello_shared.so
useHello.cpp
VSCode调试
(1)如果前面已经编译好了,只需创建launch.json文件
- Ctrl+Shift+D,点创建launch.json,选择C++(lldb/gdb)
- Add configurations选择模板C/C++: (gdb) Launch
- cwd改为“${workspaceFolder}”,即VSCode当前工作目录
{
"version": "0.2.0",
"configurations": [
{
"name": "let's debug", // debug 文件名
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/useHello", // 构建后的可执行文件
// ${workspaceFolder}是VSCode当前工作目录
"cwd": "${workspaceFolder}",
// 前面如果已经编译好了,前置任务可以注释掉
"preLaunchTask": "let's build", // 执行调试的前置任务名
"MIMode": "gdb", // Linux填gdb(与哪个 debugger 通信)
}
]
}
(2)如果前面没有编译,可以执行前置任务,创建task.json文件
- Terminal=>Configure Default Build Task,选择模板others
{
"version": "2.0.0",
"tasks": [
{// 任务1——cmake
"type": "shell",
"label": "let's cmake", // 任务名
"command": "cmake", // 命令行调用
"args": [ // 命令行参数
"../"
],
"options": {
"cwd": "${workspaceFolder}/build" // 在 build/ 目录中执行
}
},
{// 任务2——make
"type": "shell",
"label": "let's make", // 任务名
"command": "make", // 命令行调用
"options": {
"cwd": "${workspaceFolder}/build" // 在 build/ 目录中执行
},
},
{// 任务1和2——build
"label": "let's build",
"dependsOrder": "sequence", // 按列出的顺序执行
"dependsOn":[ // 依赖哪个任务的执行结果
"let's cmake",
"let's make"
]
}
]
}
(3)配置好launch.json文件和task.json文件之后,可以直接F5一键编译加调试
- F5进入调试/继续调试
- F10单步逐过程,F11单步逐语句
- shift+F11跳出