编译与构建
理解从源码到可执行文件的链路,有助于排查链接错误与性能问题。现代 C++ 项目多以 CMake 作为构建系统入口。
编译四阶段
g++ main.cpp 内部会依次执行:
- 预处理(
-E):处理#include、#define、条件编译,生成.i文件。 - 编译(
-S):源码翻译为汇编,生成.s文件。 - 汇编(
-c):汇编翻译为机器码,生成.o(目标文件)。 - 链接:把多个目标文件与库合并为可执行文件,处理符号引用。
分离头文件与实现能减少重编译时间。-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_library、target_compile_options、find_package、add_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:头文件未加inline或static。cannot bind non-const lvalue reference:把临时对象绑定到非const左值引用,需要改为const T&或延长生命周期。