gcc/g 编译器完整知识体系一、背景知识gcc 与 g 区别gcc通用编译器默认编译 C 语言处理.cpp文件不会自动链接 C 标准库不会启用 C 语法。g面向 C 的编译器驱动底层依然调用 gcc遇到 C/C 代码自动适配语法默认链接 C 标准库。通用结论编译 C 语言可用 gcc编译 C 推荐直接使用 g。C/C 源代码到程序完整四大阶段预处理 → 编译 → 汇编 → 链接二、gcc 四大编译阶段分步实操2-1 预处理Preprocess-E指令gcc -E test.c -o test.i核心工作删除注释处理#include把头文件内容直接复制进源文件宏展开#define文本替换解析条件编译#ifdef / #endif / #else添加行号信息用于编译报错定位。产物.i文件仍然是文本代码。2-2 编译Compile-S【狭义编译】指令gcc -S test.i -o test.s核心工作词法分析、语法分析、语义检查校验语法错误少分号、类型不匹配在此阶段报错将 C/C 代码翻译成汇编代码。产物.s汇编文本文件。2-3 汇编Assemble-c指令gcc -c test.s -o test.o核心工作把汇编指令翻译成 CPU 能识别的二进制机器码产物目标文件.oELF 格式二进制文件⚠️ 重点.o文件内函数地址是未填充的、待定无法直接运行。2-4 链接Linkgcc test.o -o test核心工作合并多个.o目标文件解析未定义符号找到库中对应的函数重定位填充函数、变量真实内存地址组装生成最终可执行程序。✍面试区分考点预处理 / 编译 / 汇编 只处理单个文件链接阶段才会跨文件、链接系统库。三、动态链接 和 静态链接1. 静态链接编译时将库代码完整复制到可执行程序内部开启方式编译增加参数-staticgcc main.c -o main_static -static✅优点程序独立运行运行环境不需要依赖任何库启动速度略快不需要运行时加载库。❌缺点可执行文件体积巨大多个程序使用同一个库磁盘重复存放代码库升级后必须重新编译程序无法直接更新。2. 动态链接Linux gcc 默认方式编译不复制库代码仅记录库依赖关系 等到程序运行时操作系统动态加载共享库。✅ 优点程序体积小多个进程共享内存中同一份库节省内存升级库只需替换.so文件程序无需重新编译。❌ 缺点运行环境必须存在对应版本动态库缺失则直接报错面试高频坑动态链接节省内存不是节省磁盘。四、静态库 和 动态库共享库库大量可复用代码打包而成对外只提供头文件隐藏源码。4.1 静态库libxxx.a本质多个.o目标文件用ar打包归档。制作流程# 1. 生成目标文件 gcc -c func.c -o func.o # 2. ar打包静态库 ar rcs libfunc.a func.o参数解释r插入 / 替换模块c创建库s生成索引加快链接速度使用静态库编译gcc main.c -o main main.c -L./ -lfunc-L指定库搜索目录-lfunc链接libfunc.a自动补齐 lib 前缀和.a 后缀4.2 动态库共享库libxxx.so编译参数说明-fPIC生成位置无关代码动态库必备-shared生成共享库gcc -fPIC -shared func.c -o libfunc.so使用动态库编译gcc main.c -o main -L./ -lfunc⚠重点问题编译成功运行报错找不到 so原因编译时 gcc 能通过-L找到库运行时操作系统动态链接器不会读取-L参数。解决方案三选一将 so 放入系统库目录/usr/lib临时环境变量export LD_LIBRARY_PATH./:$LD_LIBRARY_PATH修改/etc/ld.so.conf配置库路径执行ldconfig更新缓存静态库 vs 动态库对照表对比项静态库 libxxx.a动态库 libxxx.so链接时机编译期程序运行期程序大小大小运行依赖不需要原库运行环境必须存在对应 so库升级重新编译程序直接替换库文件内存占用多进程重复加载内存中只存在一份五、gcc/g 其他高频编译选项拓展了解# 指定输出文件名 -o # 调试信息gdb调试必备 -g # 开启优化 O0~O3O0无优化O3最高优化 -O1 -O2 -O3 # 警告提示 -Wall -Wextra # 指定头文件搜索路径 -I # 指定库搜索路径 -L # 链接指定库 -l # 生成位置无关代码编译动态库必备 -fPIC # 强制静态链接 -static六、配套面试真题基础问答题gcc 编译四个阶段分别是什么每个阶段做什么预处理宏展开、处理头文件→编译语法检查生成汇编→汇编生成.o 目标文件→链接合并目标文件、填充地址生成可执行程序gcc 和 g 有什么区别能不能用 gcc 编译 cpp 代码g 底层调用 gccg 自动启用 C 语法、链接 C 标准库gcc 编译 cpp 不会默认链接 std 库容易出现未定义引用错误不推荐。编译动态库为什么需要 -fPIC不加会怎么样PIC 位置无关代码。动态库会被多个进程加载到不同内存地址不加 - fPIC 生成绝对地址代码无法共享加载编译报错。进阶面试题为什么动态库编译正常运行时提示找不到共享库如何解决-L仅作用于编译阶段运行依靠动态链接器搜索路径。解决方案LD_LIBRARY_PATH、系统库目录、ldconfig 配置。.o 目标文件、可执行文件、静态库、动态库都是什么格式Linux 下全部为 ELF 格式只是类型标记不同。头文件和库有什么区别头文件 (.h)声明编译阶段使用库 (.a/.so)函数实现二进制代码链接 / 运行阶段使用。-g调试信息会影响程序性能吗不会。调试信息独立存放不会参与程序运行发布版本只需要 strip 剥离调试信息即可。-O3 优化会有什么风险编译器激进优化会调整代码执行顺序如果代码存在内存越界、未定义行为优化后 bug 表现可能和无优化版本不一致。代码 / 现象面试题使用静态库和动态库同时存在时gcc 默认优先链接哪一个默认优先动态库加-static参数强制选择静态库。多个程序使用同一个动态库内存中存在几份库代码一份只读代码段共享数据段每个进程独立。拔高思考题大厂常问什么是符号重定位发生在哪个阶段链接阶段。.o文件中调用外部函数只有符号名没有真实地址链接器找到符号真实地址回填到代码该过程为重定位。弱符号、强符号是什么链接时有什么规则gcc 默认函数是强符号变量是弱符号多个重名符号链接规则多个弱符号选任意一个强弱共存选用强符号多个强符号直接报重定义错误。