目 录CONTENT

文章目录

C++ 从源文件到可执行文件经历的过程

TalentQ
2025-09-18 / 0 评论 / 0 点赞 / 6 阅读 / 0 字

0 概述

四个步骤:预处理(Preprocessing)-> 编译(Compilation)-> 汇编(Assembly)-> 链接(Linking)

1 预处理

输入:原始的C++源文件(.cpp, .h, .hpp
输出:经过处理的“纯”C++文件(通常为 .i.ii 后缀)

g++ -E main.cc -o main.i

预处理器会对源代码进行文本级别的操作:

  • 展开头文件 (#include):将 #include 指令替换为所包含头文件的实际内容。这是一个递归的过程,如果头文件还包含了其他头文件,也会一并展开。

  • 宏替换 (#define):展开所有的宏定义,并将代码中使用的宏替换为其定义的值或代码片段。

  • 条件编译 (#if, #ifdef, #ifndef, #else, #elif, #endif):根据条件编译指令,保留满足条件的代码块,删除不满足条件的代码块。这在跨平台开发和编写Debug/Release版本时非常有用。

  • 处理特殊指令:处理 #pragma 等特殊指令。

  • 删除注释:将所有注释(///* ... */)替换为一个空格或直接删除。

2 编译

输入:预处理后的文件(.i
输出:汇编代码文件(.s

g++ -S main.i -o main.s
# 或 (可以从 .cpp 直接开始,g++ 会自动先预处理)
g++ -S main.cpp -o main.s

编译器会将“纯”C++代码翻译成特定处理器架构的汇编语言:

  • 词法分析 (Lexical Analysis):将源代码的字符序列分割成一系列的“词法单元”(token),如关键字、标识符、运算符、常量等。可以把它想象成将一句话拆分成一个个独立的单词。

  • 语法分析 (Syntax Analysis):根据C++的语法规则,将词法单元流组织成一个“抽象语法树”(Abstract Syntax Tree, AST)。如果代码有语法错误(比如缺少分号、括号不匹配),就会在这个阶段被发现并报告。

  • 语义分析 (Semantic Analysis):检查语法树在语义上是否合法,比如变量是否已声明、类型是否匹配、函数调用参数是否正确等。确保代码“言之有物”。

  • 中间代码生成与优化:生成一种中间表示(如LLVM IR),并对其进行各种优化,比如删除无用代码、计算常量表达式等,以提高最终代码的执行效率。

3 汇编

输入:汇编代码文件(.s
输出:目标文件(.o.obj

g++ -c main.s -o main.o
# 或 (可以从 .cpp 直接开始,g++ 会自动预处理和编译)
g++ -c main.cpp -o main.o

将汇编代码翻译成机器可以执行的指令,即机器码(二进制代码)。

但是,这个目标文件还不能直接运行,因为它可能引用了其他文件中的函数或变量(比如 printf),但还不知道它们的实际地址;多个目标文件还没有被组合在一起。

4 链接

输入:一个或多个目标文件(.o)、库文件(.a 静态库, .so 动态库)
输出:最终的可执行文件(a.out, .exe 或无后缀)

g++ main.o helper.o -o myprogram
  • 合并目标文件:将多个目标文件合并成一个可执行文件或库文件。

  • 符号解析 (Symbol Resolution):解析所有目标文件中的“符号”(主要是函数和变量的名字)。如果某个符号在一个目标文件中被引用,链接器必须在其他目标文件或库中找到它的定义。如果找不到,就会报“未定义的引用”(undefined reference)错误。

  • 重定位 (Relocation):在编译和汇编阶段,生成的代码和数据的地址都是从0开始的。链接器在合并所有段之后,会为它们分配最终的内存地址,并修正所有代码中对符号地址的引用,使其指向正确的位置。

  • 链接库文件

    • 静态链接:将静态库(.a)中的代码直接复制到最终的可执行文件中。优点是独立性强,缺点是文件体积大。

    • 动态链接:只在可执行文件中记录共享库(.so / .dll)的名字和引用信息,在程序运行时才由操作系统加载所需的共享库。优点是节省磁盘和内存空间,便于库的更新。

0

评论区