3

基于VSCode和CMake进行C/C++开发

 2 years ago
source link: https://star2dust.github.io/post/vscode-cmake-tutorials/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文为Linux下的C/C++开发笔记,参考b站视频BV1fy4y1b7TC

Linux指令

  • manhelp 查看指南(man = manual)
  • lstree 查看文件
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跳出

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK