计算机编译
编译运行
编译器
- 例如:gcc/g++/cl.exe/MinGW/MSVC
- 编译器的核心功能是将 C++ 源代码(.cpp)编译为机器能识别的二进制指令(.o/.obj),再通过链接器生成可执行文件(.exe 等)。下面列出来的不仅仅是编译器,已经丰富为编译套件(包含编译,链接等)。
gcc/g++
- 全称:GNU Compiler Collection(GNU 编译器套件,更是一个工具链集合)。
- 工具链组成:
工具 | 作用 |
---|---|
gcc/g++ | C/C++ 编译器,负责将源代码编译为目标文件(.o)。 |
ld | 链接器,将多个目标文件链接为可执行文件(.elf)或共享库(.so)。 |
ar | 创建静态库(.a),如 ar rcs libexample.a file1.o file2.o 。 |
as | 汇编器,将汇编代码转换为机器码。 |
make | 构建工具,解析 Makefile 并执行编译命令(通常预装在 Linux 中)。 |
GDB | 调试器,用于调试程序(如断点、单步执行等)。 |
binutils | 辅助工具集(如objdump 反汇编、nm 查看符号表)。 |
- 作用:
gcc
主要编译 C 语言代码;g++
是 gcc 的 C++ 前端,专门编译 C++ 代码(支持 C++ 标准、STL 等)。
- 特点:
- 跨平台(Linux、macOS、Windows 均可安装);
- 开源免费,支持几乎所有 C++ 标准(C++98 到 C++23);
- 是 Linux 系统默认的 C++ 编译器(比如 Ubuntu、CentOS 自带)。
MinGW
全称:Minimalist GNU for Windows(Windows 上的精简 GNU 工具集)。
作用:是 gcc/g++ 在 Windows 平台的移植版本,让 Windows 系统能使用 gcc/g++ 编译代码。
特点:
- 编译出的程序不需要依赖微软的运行时库(如 MSVC 的
msvcr120.dll
),可独立运行; - 轻量、开源,适合 Windows 平台下的跨平台开发(和 Linux 的 gcc 行为更一致)。
- 编译出的程序不需要依赖微软的运行时库(如 MSVC 的
Windows 上的 GNU 工具链移植:将 Linux 下的 GCC、GDB 等工具移植到 Windows,让 Windows 开发者能使用类 Unix 环境开发。
跨平台一致性:编译行为与 Linux/macOS 的 GCC 高度一致,适合需要跨平台的项目(如同时支持 Windows 和 Linux 的 Qt 应用)。
工具链组成:MinGW 工具链包含以下核心组件:
工具 | 作用 |
---|---|
gcc/g++ | C/C++ 编译器,负责将源代码编译为目标文件(.o)。 |
ld | 链接器,将多个目标文件链接为可执行文件(.exe)或库文件(.dll/.a)。 |
make/mingw32-make | 构建工具,解析 Makefile 并执行编译命令。 |
GDB | 调试器,用于调试程序(如断点、单步执行等)。 |
binutils | 辅助工具集(如ar 打包静态库、objdump 反汇编)。 |
- 优缺点:
优点 | 缺点 |
---|---|
无需安装 Visual Studio,轻量简洁 | 对 Windows 特定 API(如 COM、UWP)支持有限 |
编译的程序不依赖微软运行时库 | 调试工具(如 GDB)不如 Visual Studio 强大 |
与 Linux/macOS 的 GCC 兼容性好 | 对最新 C++ 标准(如 C++20/23)支持略滞后 |
适用场景:
- 跨平台开发:项目需要同时在 Windows 和 Linux 上运行,且希望使用同一套工具链。
- 轻量化需求:不需要复杂的 Windows 特性(如 UWP、DirectX),追求简单独立的程序。
- 教育 / 学习:适合新手入门,避免 Visual Studio 的复杂安装和配置。
MSVC
全称:Microsoft Visual C++(微软的 C++ 编译器)。
作用:随 Visual Studio(微软的 IDE)提供,是 Windows 平台的 “原生” 编译套件,工具链中负责编译的是cl.exe。
特点:
- 仅支持 Windows 平台;
- 对 Windows 系统 API 和微软生态(如.NET、DirectX)支持更好;
- 编译速度快,调试工具(如 VS 的调试器)更强大,但闭源且仅绑定 Visual Studio。
Windows 原生 C++ 工具链:微软为 Windows 平台开发的官方工具链,深度集成 Windows 系统 API 和特性。
Visual Studio 生态:与 Visual Studio IDE 紧密结合,提供强大的调试、代码分析等功能。
工具链组成:MSVC 工具链是 Visual Studio 的一部分,包含:
工具 | 作用 |
---|---|
cl.exe | C/C++ 编译器(CL = Compiler),替代 GCC 的g++ 。 |
link.exe | 链接器,替代 GCC 的ld 。 |
nmake | 构建工具,类似 GNU 的make ,但语法略有不同。 |
Visual Studio 调试器 | 图形化调试工具,支持断点、内存分析、性能分析等高级功能。 |
MSBuild | 微软的项目构建系统,替代传统的 Makefile,支持.vcxproj 格式。 |
Windows SDK | 包含 Windows API 头文件和库文件(如 Win32 API、COM、DirectX)。 |
- 优缺点:
优点 | 缺点 |
---|---|
对 Windows 系统 API(如 UWP、DirectX)支持完美 | 必须安装 Visual Studio(占用空间大,安装复杂) |
调试工具(如 VS 调试器)功能强大 | 编译的程序依赖微软运行时库(如msvcr120.dll ) |
对最新 C++ 标准支持更及时 | 不支持跨平台(只能编译 Windows 程序) |
适用场景:
Windows 原生应用:开发依赖 Windows 特定功能的程序(如 UWP 应用、DirectX 游戏)。
企业级开发:团队使用 Visual Studio 协作,需要强大的调试和代码分析工具。
高性能需求:MSVC 对 Windows 系统调用的优化更好,适合性能敏感的程序(如游戏、数据库)。
维度 | MinGW | MSVC |
---|---|---|
依赖库 | 不依赖微软运行时库,程序可独立运行 | 依赖微软运行时库(需随程序分发) |
调试体验 | 依赖 GDB,功能较基础 | Visual Studio 调试器功能强大 |
C++ 标准支持 | 略滞后于 MSVC(如对 C++20 的 concept 支持稍慢) | 对最新标准支持更及时 |
Windows API 支持 | 有限(仅支持部分 Win32 API) | 完整支持所有 Windows 特性(包括 UWP、COM) |
跨平台兼容性 | 与 Linux/macOS 的 GCC 兼容 | 仅支持 Windows 平台 |
安装复杂度 | 轻量(单独安装包,通常 < 1GB) | 重量级(需安装 Visual Studio,通常 > 5GB) |
编译器选项
- Windows 系统:
- 选项通常有 “MinGW 11.2.0”“MSVC 2019”“MSVC 2022” 等。
- MinGW:适合新手,无需额外安装 Visual Studio,编译的程序可独立运行;
- MSVC:需要先安装对应版本的 Visual Studio(如选 MSVC 2022,需先装 VS2022),适合习惯用 VS 调试的用户。
- Linux 系统:
- 通常是 “GCC 11”“GCC 12” 等(即 g++),直接选系统默认的 gcc 版本即可。
- macOS 系统:
- 通常是 “Clang 14”(苹果基于 LLVM 的编译器,类似 gcc,macOS 默认)。
构建工具
- 例如:make/MSBuild/QMake/Ninja
- 负责自动化编译流程
- 写代码时,手动输入
g++ main.cpp -o app
编译单个文件很简单,但大型项目(如 Qt 程序)有上百个源文件,需要自动化编译(按依赖顺序编译、链接),这就是构建工具的作用。
1. make
- 作用:根据
Makefile
(定义编译规则的文本文件)自动执行编译命令(如gcc -c file.cpp
),解决多文件依赖问题。 - 特点:
- 历史悠久,是 Unix/Linux 下的标准构建工具;
- 但
Makefile
需要手动编写,且语法复杂,跨平台兼容性差(Windows 上需要额外安装 MinGW 或 Cygwin 才能用)。
2. MSBuild
- MSBuild 是Windows 平台的原生构建系统
- 定位
- 解析 Visual Studio 项目文件(
.vcxproj
、.csproj
等),并执行编译、链接等操作; - 与 Visual Studio IDE 深度集成,但也可独立通过命令行使用;
- 是微软技术栈(如.NET、C++/CLI、UWP)的官方构建工具。
- 解析 Visual Studio 项目文件(
- 定位
- 工作流程:
- 解析
.vcxproj
中的规则; - 调用 MSVC 编译器(
cl.exe
)编译源码; - 调用链接器(
link.exe
)生成可执行文件或库。
- 解析
3. Ninja
- 定位:轻量级、高性能的构建工具,通常与 CMake 配合使用。
- 工作流程:
- 用 CMake 生成 Ninja 构建文件(而非 Makefile):
cmake -G "Ninja"
; - 执行
ninja
编译项目。
- 用 CMake 生成 Ninja 构建文件(而非 Makefile):
- 优点:
- 编译速度比
make
快(并行构建优化更好); - 适合大型项目(如 Qt 自身源码编译)。
- 编译速度比
CMake
构建工具的局限性
- MinGW 中的
mingw32-make
、MSVC 中的MSBuild
属于 “底层构建工具”,它们的工作是解析特定格式的构建文件 (如 Makefile、.vcxproj),并按规则执行编译、链接命令。但它们有明显短板:
平台 / 工具链绑定
make
/mingw32-make
依赖Makefile(语法复杂,且 Windows/Linux 下的写法有差异);nmake
/MSBuild
依赖Visual Studio 的项目文件(.vcxproj,仅支持 MSVC 工具链,无法在 Linux 上用)。 若项目需要同时支持 MinGW、MSVC、Linux 的 GCC、macOS 的 Clang,你需要为每个工具链写一套独立的构建文件(如 Makefile、.vcxproj、Xcode 项目),维护成本极高。
复杂项目管理困难
- 对于多文件、多模块、多依赖库(如链接 Qt 库、第三方库)的项目,手动编写 Makefile 或.vcxproj 容易出错(比如路径配置、依赖顺序、条件编译逻辑);
- 大型项目(如 Qt 本身、OpenCV)的构建规则复杂,手动维护几乎不可能。
CMake 的核心作用
[!NOTE] 统一生成跨平台构建文件
- CMake 不直接编译代码,而是根据一份统一的配置文件(CMakeLists.txt),自动生成对应工具链所需的构建文件(如 Makefile、.vcxproj、Xcode 项目)。简单说:
- 只需要写一份
CMakeLists.txt
,声明 “源代码在哪、依赖哪些库、编译选项是什么”; - CMake 会根据你当前的工具链(如 MinGW/MSVC/GCC/Clang)和平台(Windows/Linux/macOS),自动生成该工具链能识别的构建文件(比如给 MinGW 生成 Makefile,给 MSVC 生成.vcxproj,给 Linux 生成 GCC 的 Makefile)。
- 只需要写一份
- 作用:跨平台的构建系统生成器(比 make,MSBuild 更高层)。
- 工作流程:
- 开发者编写
CMakeLists.txt
(简单的跨平台配置文件),定义项目结构、编译选项等; - CMake 根据
CMakeLists.txt
,自动生成对应平台的构建文件(比如 Linux 的Makefile
、Windows 的 Visual Studio 项目文件、macOS 的 Xcode 项目); - 再通过对应平台的构建工具(make、VS、Xcode)执行编译。
- 开发者编写
- 特点: 解决了 make 的跨平台痛点,是目前大型 C++ 项目的主流构建工具
- MinGW 和 MSVC 包含的构建工具(如
make
/mingw32-make
、nmake
/MSBuild
)是直接执行编译流程的工具,而 CMake 是 “生成构建工具所需文件的工具”(元构建系统,Meta-build System)。两者的定位完全不同,CMake 的核心价值是解决跨平台、跨工具链的构建兼容性问题 ,尤其在复杂项目中不可替代。
场景 | 直接用构建工具(make/MSBuild) | 用 CMake |
---|---|---|
跨平台支持 | 需要手动写多套构建文件(维护成本极高) | 一份 CMakeLists.txt 适配所有平台 |
多工具链切换(MinGW→MSVC) | 需重新编写构建文件 | 只需重新生成一次构建文件(一键切换) |
复杂项目(多依赖 / 多模块) | 手动维护容易出错,依赖关系混乱 | 自动处理依赖、路径、编译选项,结构清晰 |
团队协作 | 不同成员可能用不同工具链,构建规则不统一 | 统一配置文件,避免 “在我这能编,在你那报错” |
CMake 与构建工具的关系
- 构建工具(
make
/MSBuild
等)是 “工人”,负责按图纸(Makefile/.vcxproj)施工(编译代码); - CMake 是 “图纸设计师”,根据你的需求(CMakeLists.txt),为不同 “工人”(工具链)绘制对应的 “施工图纸”(构建文件)。
CMake优势
如果项目需要同时支持Windows(MinGW/MSVC)、Linux(GCC)、macOS(Clang),直接用make
或MSBuild
几乎无法实现:
- 不同平台的库路径(如 Qt 库在 Windows 是
C:\Qt\...
,在 Linux 是/usr/lib/qt5/...
)、编译选项(如 Windows 需要-DWIN32
,Linux 需要-DLINUX
)、链接方式(如 Windows 的.dll
和 Linux 的.so
)差异极大; - 用 CMake 的话,只需在
CMakeLists.txt
中通过find_package(Qt5 COMPONENTS Core Widgets)
声明依赖,CMake 会自动适配不同平台的 Qt 库路径和编译规则,无需手动修改。
即使只在 Windows 开发,若需要在MinGW 和 MSVC 之间切换(比如测试不同工具链的兼容性),CMake 能避免重复劳动:
- 无需为 MinGW 写一套 Makefile,再为 MSVC 写一套.vcxproj;
- 只需运行
cmake -G "MinGW Makefiles"
生成 MinGW 的构建文件,或cmake -G "Visual Studio 17 2022"
生成 MSVC 的.vcxproj,同一份CMakeLists.txt
通用。
GCC
GCC 编译器通常以 Linux 命令的形式在终端(Shell)中使用。
如果需要其他特定的GCC组件,例如C++编译器(g++)或Fortran编译器(gfortran),需要额外再安装。
指令格式:gcc [options] [filenames]
选项 | 说明 |
---|---|
-c | 只编译源代码,生成目标文件(xx.o)而不进行链接 |
-E | 只进行预处理,生成预处理后的源代码文件 |
-O | 优化生成的代码,可以使用-O1、-O2或-O3进行不同级别的优化(是大写字母O) |
-g | 生成调试信息,以便进行源代码级调试 |
-Wall | 显示编译时的警告信息 |
-std | 指定所使用的C语言标准,如-std=c11 |
-I | 指定包含头文件的目录 |
-L | 指定链接库文件的目录 |
-l | 链接指定的库文件 |
GDB
- 要想使用gdb,需要在编译时开启调试功能
gcc:在编译时添加 -g 选项生成调试信息
gcc -g -o program program.c(-g:生成调试符号,便于 GDB 进行源代码级调试。)
Makefile:大项目时添加
CC = gcc
CFLAGS = -Wall -g
CMakeLists.txt:构建工具
对于C++:
在CMakeLists.txt文件中添加如下语句:
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
对于C:
在CMakeLists.txt文件中添加如下语句:
SET(CMAKE_BUILD_TYPE "Debug")
SET(CMAKE_C_FLAGS_DEBUG "$ENV{CFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_C_FLAGS_RELEASE "$ENV{CFLAGS} -O3 -Wall")
- gdb指令
类型 | 说明 | 指令 |
---|---|---|
安装 | 查看是否安装了gdb | rpm -qa|grep gdb |
查看安装版本 | gdb -v | |
启动 | 启动gdb | gdb program |
退出 | 退出gdb | quit;q |
显示当前文件源码 | 显示第linenum行的上下文内容 | list linenum |
显示函数名为function的函数的源程序 | list function | |
显示当前行后面的源程序 | list | |
显示当前文件开始处的源程序 | list - | |
显示其他文件源码 | 显示file文件下第linenum行 | list file:linenum |
显示file文件的函数名为function的函数的源程序 | list file:function | |
显示源码设置 | 设置一次显示源代码的函数(一般打印当前行的上5行和下5行,默认是10行) | set listsize count |
查看当前listsize的设置 | show listsize | |
断点 | 设置断点 | break;b |
当前文件打断点 | 在第10行设置断点 | b 10 |
在func函数入口处设置断点 | b func | |
设置临时断点(temporary breakpoint),即断点只会在首次触发后被自动删除 | tbreak <location>;例如b xxx.c:n if intValue ==5表示 如果intValue的值等于5,在xxx.c文件第n行中设置断点 | |
其他文件打断点 | 在源文件为filename的linenum行设置断点 | b filename:linenum |
在源文件filename的function函数的入口处设置断点 | b filename:function | |
断点查询 | 查询所有断点 | info b;info break; i b |
删除断点 | 删除所有的断点 | delete |
删除断点为num的断点 | delete num | |
删除不连续的断点 num1 num3 | delete num1 num3 | |
删除连续的断点,删除 n-m的断点 | delete n-m | |
禁用断点 | 指定断点无效,不会删除断点 | disable[range…];dis [range…] |
启用断点 | 指定断点有效,对应解开disable设置的无效断点 | enable[range…];ena[range…] |
运行控制 | 执行代码 | run;r |
退出进入的函数,执行到当前函数返回为止 | finish;fin | |
循环体内运行程序,直到退出循环体 | untile;u | |
继续运行程序,如果有断点,则调到下一个断点处 | continue;c | |
单步调试 | 函数调用当做一条简单语句执行 | next;n |
函数调用进入被调用函数体内 | step;s | |
查看运行时变量的值 | 打印var的值 | print var;p var |
打印var的地址 | print &var;p &var | |
查看var的类型 | ptype var | |
自动显示变量的值,当程序停住时,或在单步追踪时,这些变量会自动显示 | 设置自动显示 | display 变量名 |
查看display设置的自动显示的信息 | info display | |
删除自动显示 | undisplay num(info display时显示的编号) | |
删除自动显示 | delete display num | |
禁用num显示 | disable display num | |
启用num显示 | enable display num | |
监视变量 | 监视变量var的值,当变量的值发生改变时,停止程序的执行 | watch var;w var |
查看watch设置的监视点的信息 | info watchpoints | |
禁用num监视 | disable num | |
启用num监视 | enable num | |
堆栈跟踪 | 显示当前函数调用的堆栈跟踪信息 | backtrace;bt |
查看当前调试状态的信息 | 查看断点 | info break;i b |
查看display | info display | |
查看watch | info w | |
查看函数的参数列表 | info args | |
查看当前函数的局部变量 | info locals | |
查看寄存器的值 | info registers;i r | |
显示当前线程的列表 | info threads | |
显示当前进程接收到的信号 | info signals |
- 在 Linux 操作系统中,当程序执行发生异常崩溃时,系统可以将发生崩溃时的内存数据、调用堆栈情况等信息自动记录下载,并存储到一个文件中,该文件通常称为 core 文件,Linux 系统所具备的这种功能又称为核心转储(core dump)。GDB 对 core 文件的分析和调试提供有非常强大的功能支持,也可以通过 GDB 调试产生的 core 文件来快速解决问题。
- 查看是否开启 core dump 这一功能 :ulimit -a
- core file size(core 文件大小)对应的值为 0,表示当前系统未开启 core dump 功能
- 开启 core dump:ulimit -c unlimited (unlimited 表示不限制 core 文件的大小)
CMakLists
cmake_minimum_required(VERSION 3.15)
# 项目信息
project(MyProject VERSION 1.0 LANGUAGES CXX)
# 设置默认构建类型为 Debug
set(CMAKE_BUILD_TYPE Debug)
# 指定 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 针对 Debug 模式的编译选项
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0")
set(CMAKE_C_FLAGS_DEBUG "-g -O0")
# 添加可执行目标
add_executable(my_executable main.cpp)