跳到主要内容

编译与构建

理解从源码到可执行文件的链路,有助于排查链接错误与性能问题。现代 C++ 项目多以 CMake 作为构建系统入口。

编译四阶段

g++ main.cpp 内部会依次执行:

  1. 预处理(-E):处理 #include#define、条件编译,生成 .i 文件。
  2. 编译(-S):源码翻译为汇编,生成 .s 文件。
  3. 汇编(-c):汇编翻译为机器码,生成 .o(目标文件)。
  4. 链接:把多个目标文件与库合并为可执行文件,处理符号引用。

分离头文件与实现能减少重编译时间。-c 编译为目标文件,-o 指定输出名,-I 追加头文件搜索路径,-L 追加库搜索路径,-l 链接库。

CMake 基础

CMake 通过 CMakeLists.txt 描述构建,再生成 Makefile 或 Ninja 等原生构建文件。最小项目示例:

cmake_minimum_required(VERSION 3.16)
project(app LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(app main.cpp utils.cpp)
target_include_directories(app PRIVATE include)
target_link_libraries(app PRIVATE fmt::fmt)

常用命令:add_librarytarget_compile_optionsfind_packageadd_subdirectory。CMake 3.5+ 推荐使用「目标级」配置(target_*),避免全局污染。

Make 简介

Make 是经典构建工具,Makefile 定义规则:target: deps 后跟构建命令。变量用 $(VAR) 引用,自动变量 $@(目标)、$<(首个依赖)、$^(所有依赖)简化命令。

app: main.o utils.o
g++ -o $@ $^

%.o: %.cpp
g++ -c -o $@ $<

大型项目通常用 CMake/Ninja 替代手写 Makefile。

ABI 概念

ABI(Application Binary Interface)规定函数调用约定、名称修饰、布局与运行时行为。同一 ABI 下不同编译器生成的目标文件才能相互链接。GCC 与 Clang 在 Linux 上默认使用 Itanium C++ ABI,MSVC 使用自己的 ABI。混用不同 ABI 的库会导致链接错误或未定义行为。

常见编译错误

  • undefined reference:链接阶段找不到符号,通常是未链接目标文件或库。
  • multiple definition:头文件中实现了非内联函数,违反单一定义规则。
  • redeclared with different visibility:头文件未加 inlinestatic
  • cannot bind non-const lvalue reference:把临时对象绑定到非 const 左值引用,需要改为 const T& 或延长生命周期。