文章目录
C/C++编译与VSCode配置
主流的编译器有 GUN 大名鼎鼎的类 UNIX 系统下的编译器 gcc、gcc 的 windows 的移植版 mingw、微软的 MSVC(对新标准支持的少)和苹果的 clang 等,不同编译器的使用区别主要是所支持的 C/C++ 语言标准不同
编译机制
GCC 编译 hello.c 文件,分四步完成编译(G++ 编译 hello.cpp 同理)
-
预处理 gcc -E hello.c -o hello.i,不检查语法错误,将C文件转为预处理文件
将头文件拷贝至源文件(头文件展开)、处理所有的条件预编译指令、丢弃注释和宏替换等
-
编译 gcc -S hello.i -o hello.s,检查语法错误,将预处理文件转为汇编文件
将预处理完的.i文件进行一系列的词法分析、语法分析、语义分析及优化后生成响应的汇编代码文件,这是整个程序构建的最核心的部分,也是最复杂的部分
-
汇编 gcc -c hello.s -o hello.o,将汇编文件转为二进制文件
将编译生成的.o文件编程机器可执行的指令,每一个汇编语句几乎都对应一条机器指令
-
链接 gcc hello.o -o hello,链接动态库与静态库,设置运行环境
-
gcc -g hello.c -o hello,可直接实现上述四步编译
参数名 | 含义 |
---|---|
-E | 预处理 |
-S | 编译 |
-c | 汇编 |
-g | 输出调试信息 |
-o | 指定输出文件名 |
.c | C语言文件 |
.i | 预处理后的C语言文件 |
.s | 汇编文件 |
.o | 目标文件 |
VSCode配置
VSCode是一个编辑器,配合插件才能搭建一个集成开发环境,因此需要配置文件实现联动,对于C与CPP是四个配置文件(它们分别包括用户和工作区两种,作用域上用户是全局的而工作区只用于当前,优先级上工作区设置会覆盖用户设置)
settings.json
VSCode 的方方面面基本都能在其中配置,如插件 Code Runner 等的配置
{
"code-runner.runInTerminal": true, // 设置成false会在“输出”中输出,无法输入
"code-runner.executorMap": {
"c": "cd $dir && clang '$fileName' -o '$fileNameWithoutExt.exe' -Wall -g -O2 -static-libgcc --target=x86_64-w64-mingw -std=c11 && &'$dir$fileNameWithoutExt'",
"cpp": "cd $dir && clang++ '$fileName' -o '$fileNameWithoutExt.exe' -Wall -g -O2 -static-libgcc --target=x86_64-w64-mingw -std=c++17 && &'$dir$fileNameWithoutExt'"
},
"code-runner.saveFileBeforeRun": true, // run code前保存
"code-runner.preserveFocus": true, // 若为false,run code后光标会聚焦到终端上
"code-runner.clearPreviousOutput": false, // 每次run code前清空属于code runner的终端消息
"code-runner.ignoreSelection": true, // 默认为false,效果是鼠标选中一块代码后可以单独执行,但C是编译型语言,不适合这样用
"clang.cflags": [ // 控制c语言静态检测的参数
"--target=x86_64-w64-mingw",
"-std=c17",
"-Wall"
],
"clang.cxxflags": [ // 控制c++静态检测时的参数
"--target=x86_64-w64-mingw",
"-std=c++98",
"-Wall"
],
}
c_cpp_properties.json
用于编程时的智能感知,配置编译器路径,C与CPP的标准等,通常使用默认的设置即可,如果你自己编写了头文件又不在workspaceFolder下,或是使用别人的库,就需要将这些路径加到includePath和browse里
{
"configurations": [
{
"name": "MinGW",
"intelliSenseMode": "gcc-x64",
"compilerPath": "C:/LLVM/bin/gcc.exe",
"includePath": [
"${workspaceFolder}"
],
"defines": [],
"browse": {
"path": [
"${workspaceFolder}"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c17",
"cppStandard": "c++98"
}
],
"version": 4
}
tasks.json
用于配置任务,比如配置编译,因为C与CPP编译后才能进行调试等操作
// https://code.visualstudio.com/docs/editor/tasks
{
"version": "2.0.0",
"tasks": [
{
"label": "C/C++生成活动文件", // 任务名称,与launch.json的preLaunchTask相对应
"type": "cppbuild", // 任务执行环境类型,可指令shell命令被解释为shell命令如bash、cmd 和PowerShell等,或指定process则命令被解释为要执行的进程
"command": "gcc", // 实际要执行的命令,C还可选择clang,C++用clang++,g++
"args": [
"${file}",
"-fdiagnostics-color=always", // 使用彩色错误提示
"-o", // 指定输出文件名,不加参数Windows默认a.exe,Linux下默认a.out
"${fileDirname}/${fileBasenameNoExtension}.exe",
"-g", // 生成和调试有关的信息使得编译后可以调试
"-Wall", // 开启额外警告
"-static-libgcc", // 静态链接libgcc
"--target=x86_64-w64-mingw", // 不加这一条就会找不到头文件
"-std=c17" // 根据自己的需要的标准进行修改
], // 编译命令参数
"group": {
"kind": "build",
"isDefault": true // 设为false可做到一个tasks.json配置多个编译指令
},
"presentation": {
"echo": true,
"reveal": "always", // 在终端中显示编译信息的策略,可以为always,silent,never
"focus": false, // 设为true后可以使执行task时焦点聚集在终端
"panel": "shared" // 不同的文件的编译信息共享一个终端面板,可以设为new
}
}
]
}
launch.json
用于调试的配置文件,可以有多个配置块,每个块都可以用于指定一个调试语言环境与类型等,每个中必须配置type、request和name这三项(不管用什么编程环境都必须配置)
-
type,指定编程环境,比如c、cpp、java、python和node等
-
request,指定调试模式,VSCode只有launch与attach
以launch方式启动的时候VSCode直接从本地项目启动并自动添加一个调试器;以attach方式启动,通常需要调试一个已经在跑的项目(web服务)且可能是在远程或者在本地但是重启速度很慢的项目,因为项目在远程上不好打断点,但是可以通过attach启动的方式在这个远程的服务外面包裹一层调试器,间接达到调试的目的
-
name,定义调试的名字
-
调试工具有gdb(GUN)、msvcdbg(微软)和lldb(苹果)等
// https://github.com/Microsoft/vscode-cpptools/blob/master/launch.md
{
"version": "0.2.0",
"configurations": [
{
"name": "gdbdebug", // 配置名称,将会在启动配置的下拉菜单中显示
"type": "gdb", // 调试器的类型
"request": "launch", // 请求配置类型可以为launch或attach
"program": "${fileDirname}/${fileBasenameNoExtension}.exe", // 将要进行调试的程序的路径
"args": [], // 程序调试时传递给程序的命令行参数,一般设为空即可
"stopAtEntry": false, // 设为true时程序将暂停在程序入口处,相当于在main上打断点
"cwd": "${workspaceFolder}", // 调试程序时的工作目录,此为工作区文件夹;改成${fileDirname}可变为文件所在目录
"environment": [], // 环境变量
"externalConsole": true, // 为true时使用单独的cmd窗口
"internalConsoleOptions": "neverOpen", // 如果不设为neverOpen,调试时会跳到“调试控制台”选项卡
"MIMode": "gdb", // 指定连接的调试器
"miDebuggerPath": "gdb.exe", // 调试器路径
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": false
}
],
"preLaunchTask": "C/C++生成活动文件" // 调试会话开始前执行的任务,一般为编译程序,与tasks.json的label相对应
}
]
}
VSCode中预定义变量名的含义
变量名 | 含义 |
---|---|
${workspaceRoot} | 当前打开的文件夹的绝对路径+文件夹的名字 |
${workspaceRootFolderName} | 当前打开的文件夹的名字 |
${file} | 当前打开文件的绝对路径 |
${relativeFile} | 从当前打开的文件夹到当前打开的文件的相对路径 |
${workspaceFolder} | 当前工作区的文件夹路径 |
${fileBasename} | 当前打开的文件名加后缀名,不包括路径 |
${fileBasenameNoExtension} | 当前打开的文件的文件名,不包括路径和后缀名 |
${fileDirname} | 当前打开的文件所在的绝对路径,不包括文件名 |
${fileExtname} | 当前打开的文件的后缀名 |
${cwd} | 任务开始运行时的当前工作目录 |
${lineNumber} | 当前打开的文件,光标所在的行数 |
/* | 拦截当前的全部文件夹 |
/** | /* 的递归,拦截当前的全部文件夹和全部的子文件夹 |
CMake构建Makefile实现编译
CMake 是一个跨平台、开源的构建系统,是集软件构建、测试、打包于一身的软件,使用与平台和编译器独立的配置文件对软件编译过程进行控制
CMake前言
Make 是工程管理器,可以解释 Makefile 中指令的命令工具,通过读入 Makefile 文件的内容来实现自动化编译,并且能够根据文件时间戳自动发现更新过的文件而只编译改动的代码文件,而不用每次都全部编译
Makefile 关系到了整个工程的编译规则,一个工程中的源文件不计其数,并且按类型、功能和模块分别放在若干个目录中,Makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,Makefile 很像一个 Shell 脚本,可以执行操作系统的命令
有许多 Make 工程管理器,如 GNU Make、QT 的 qmake、微软的 MS nmake、BSD Make、Makepp 等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别,如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile
CMake就是针对上面问题所设计的工具,它首先允许开发者编写一种平台无关的 CMakeLists.txt 文件来设置整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化Makefile和工程文件,如Unix的Makefile或Windows的Visual Studio工程,从而做到一次编写到处运行,一些使用CMake作为项目架构系统的知名开源项目有VTK、ITK、KDE、OpenCV、OSG等
CMake命令
CMake 命令一般格式 command(<target> [E] A|B|C)
参数间空格隔开;尖括号 <>
必选变量;<target>
;方括号 []
可选变量 [E]
;竖线 |
或 A|B|C
;通过 ${<变量>}
引用变量
CMake 预定义了一些变量
PROJECT_SOURCE_DIR
工程的根目录PROJECT_BINARY_DIR
运行CMake命令的目录,默认是${PROJECT_SOURCE_DIR}/build
CMAKE_INCLUDE_PATH
环境变量CMAKE_LIBRARY_PATH
环境变量CMAKE_C_COMPILER
C 编译器;CMAKE_CXX_COMPILER
C++ 编译器CMAKE_C_STANDARD
C 标准;CMAKE_C_STANDARD
C++ 标准CMAKE_CURRENT_SOURCE_DIR
当前处理的CMakeLists.txt
所在的路径CMAKE_CURRENT_BINARY_DIR
目标编译目录;SET(EXECUTABLE_OUTPUT_PATH 自定义路径)
重新定义目标二进制可执行文件的存放位置,不会影响此变量,只是改变最终目标文件的存储路径LIBRARY_OUTPUT_PATH
重新定义目标链接库文件的存放位置SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
,可以使用include
命令调用文件或模块的 CMake 代码
CMake 的常用命令
命令 | 描述 |
---|---|
cmake_minimum_required(VERSION <版本号>) | 指定 CMake 最低版本 |
project(<工程名字变量>) | 指定项目名字赋给 PROJECT_NAME 中 |
set(<变量> [值一] [值二]...[值某某]) | 设置变量并赋值且可赋多值 |
message(<变量|值>) | 输出信息 |
add_definitions(-D<变量> -D<变量>=<值>) | 增加宏定义 |
option(<变量> "<help_text>" ON|OFF) | 设置编译开关 |
enable_language(ASM) | 添加汇编文件支持 |
aux_source_directory(<路径> <变量>) | 把路径中的所有源文件的全路径赋给变量 |
file(GLOB <变量> "<源文件路径>/*.h|*.c") | 通过匹配 globbing 表达式把路径中匹配的文件全路径赋给变量 |
add_library(<变量> SHARED|STATIC <源文件名字>) | 生成动态库或静态库 |
include_directories(<路径>) | 提供了一个搜索头文件的目录 |
find_library(<变量> <库名字> <路径>) | 从指定路径搜索库名字并把全路径赋给变量 |
find_package(<包名字> REQUIRED) | 用于搜索外部库且设置为必须找到 |
target_link_libraries(<目标名字> <库名字>) | 给目标链接依赖的静态库或动态库可以传递 |
add_executable(<目标名字> <源文件>) | 生成可执行文件 |
CMake举例
一个工程文件夹 VSCode 中的项目 0319 文件夹中分别有 1 与 2 两个文件夹包含源文件作为各自独立脚本
- 编程 CMakeLists.txts 与项目 0319 文件夹放在同级目录)
# 设置CMake的版本号
cmake_minimum_required(VERSION 3.20)
# 设置项目名字
project(Code1)
project(Code1)
# 设置项目的编程语言的标准和编译器
set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 98)
set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")
# 可以通过匹配globbing表达式设置源文件路径
# file(GLOB SRC_FILES
# "${PROJECT_SOURCE_DIR}/src/*.h"
# "${PROJECT_SOURCE DIR}/src/*.cpp"
# "${PROJECT_SOURCE DIR}/src/*.c"
# "${PROJECT_SOURCE DIR}/src/*.cc")
# 设置源文件路径
aux_source_directory(0319/1 CFile1)
aux_source_directory(0319/2 CFile2)
# 设置项目的需要执行的源文件
add_executable(Code1 ${CFile1})
add_executable(Code2 ${CFile2})
-
通过 CMake 执行 CMakeLists.txts 构建 Makefile 与项目并完成编译
在 VSCode 终端执行
mkdir CMakeBuild
创建文件夹,再cd CMakeBuild
进入并执行cmake ..
对上层文件夹构建 Makefile 并与源码分离存储在 CMakeBuild 中(CMakeLists.txt 所在目录一般在根目录下,直接运行 cmake .构建 Makefile 中间文件将会和源文件混杂此时最好在 CMakeLists.txt 编程一些自动化移动逻辑管理好项目树)接着执行make
批处理源文件完成编译