【Linux】编译器gcc/g++

本文面向 C/C++ 初学者,系统梳理 gcc/g++ 编译器的核心用法。全文围绕编译四大阶段(预处理、狭义编译、汇编、链接)展开,并深入对比静态库与动态库的区别,帮助读者理解从源文件到可执行文件的完整链路。此外,还整理了高频查看命令、头文件防重复包含写法、常用 gcc 选项速查表以及易混易错点,最后给出最简实操串。读者可按需定位到对应章节,快速掌握 gcc/g++ 的实用技能。

一、基础区分

  • gcc :面向 C 语言 的编译器
  • g++ :面向 C++ 语言 的编译器

补充:gcc 也可以编译 C++,但不会自动链接 C++ 标准库;日常 C++ 开发优先直接用 g++。

二、C/C++ 程序编译四大阶段

下面用一张流程图直观展示从源文件到可执行文件的完整编译链路:

flowchart TD A[源文件 test.c / test.cpp] -->|gcc -E| B[预处理产物 test.i] B -->|gcc -S| C[汇编代码 test.s] C -->|gcc -c| D[目标文件 test.o] D -->|gcc test.o -o test| E[可执行文件 test] A -. 阶段1:预处理 -E .-> A1[头文件展开 / 宏替换 / 删除注释] B -. 阶段2:狭义编译 -S .-> B1[词法 / 语法 / 语义分析,生成汇编] C -. 阶段3:汇编 -c .-> C1[汇编指令翻译成二进制机器码] D -. 阶段4:链接 .-> D1[合并 .o / 解析符号 / 重定位 / 链接系统库]

源文件 .c / .cpp 分阶段执行,gcc 参数控制停在对应阶段

  1. 预处理(Preprocess) -E

    cpp 复制代码
    gcc -E test.c -o test.i
    • 工作:头文件 #include 展开、#define 宏替换、删除注释、#ifdef 条件编译筛选分支、处理 #pragma
    • 产物:.i 文件,纯文本,没有语法检查
  2. 狭义编译(Compile) -S

    cpp 复制代码
    gcc -S test.i -o test.s
    • 工作:词法分析、语法分析、语义检查,生成抽象语法树 AST,翻译成汇编指令
    • 产物:.s 汇编代码文件,报错大多集中在这个阶段
  3. 汇编(Assemble) -c

    cpp 复制代码
    gcc -c test.s -o test.o
    • 工作:汇编指令翻译成二进制机器码
    • 产物:可重定位目标文件 .o
    • 特点:包含机器码、符号表、重定位信息;符号地址未补齐,不能直接运行
  4. 链接(Link)

    cpp 复制代码
    gcc test.o -o test
    • 工作:合并多个 .o 目标文件并解析符号引用、重定位、链接系统库
    • 产物:ELF 格式可执行文件 / 库文件

一键编译:gcc test.c -o test,内部自动走完上面 4 步,不保留中间 .i / .s / .o

三、静态库与动态库

链接阶段的核心任务之一是把代码中用到的库函数合并进最终产物,而库分为静态库和动态库两种,二者在链接时机、产物形态和使用方式上差异明显。

3.1 静态库(Static Library)

  • 概念 :在链接阶段 把库中需要的目标文件代码直接复制合并进可执行文件,生成后不再依赖库文件。
  • 常见后缀 :Linux 下为 .a(如 libm.a),Windows 下为 .lib。
  • 生成方式 :先用 gcc -c 编译出目标文件,再用 ar 归档工具打包。
cpp 复制代码
# 编译目标文件
gcc -c add.c -o add.o
打包成静态库
ar rcs libadd.a add.o
链接静态库
gcc main.c -L. -ladd -o main
  • 优点 :程序运行时不依赖外部库文件,部署简单;加载速度快。
  • 缺点 :可执行文件体积较大 ;库更新后需要重新编译链接整个程序。

3.2 动态库(Dynamic Library / Shared Library)

  • 概念 :链接阶段只记录库的引用信息 ,不复制代码;程序运行时才由系统动态加载库文件到内存。
  • 常见后缀 :Linux 下为 .so(如 libm.so),Windows 下为 .dll。
  • 生成方式 :编译时加 -fPIC 生成位置无关代码,再用 -shared 生成动态库。
cpp 复制代码
# 编译位置无关的目标文件
gcc -c -fPIC add.c -o add.o
生成动态库
gcc -shared add.o -o libadd.so
链接动态库
gcc main.c -L. -ladd -o main
  • 优点 :可执行文件体积小 ;多个程序可共享同一份库 ,节省内存;库升级时无需重新编译主程序。
  • 缺点 :运行时依赖库文件存在,缺失会报错;部署时需要一并携带库文件。

3.3 静态库与动态库对比

对比项 静态库 动态库
链接时机 编译链接阶段 程序运行时
Linux 后缀 .a .so
Windows 后缀 .lib .dll
可执行文件体积 较大 较小
运行时依赖 不依赖库文件 依赖库文件
库升级 需重新编译链接 替换库文件即可

补充:链接时若同时存在同名静态库和动态库,gcc 默认优先选择动态库 ;可用 -static 强制全部使用静态链接。

code>.dll动态库、.lib静态库

项目 动态链接(默认) 静态链接(-static)
命令 gcc test.c -o test gcc test.c -o test_static -static
原理 编译时只记录库符号;程序运行时才加载.so 共享库 链接阶段,把用到的库代码直接拷贝进可执行文件
程序体积 小 大
依赖关系 运行环境必须存在对应的.so库;库丢失程序无法启动 打包后独立,不依赖外部库,移植方便
资源占用 多个程序共享同一份库,节省内存磁盘 每个程序自带一份库代码,存在代码冗余
查看命令 ldd test 查看依赖的动态库 file test_static 显示 statically linked;ldd 提示不是动态程序

⚠️ 注意:很多精简系统默认没有安装静态库,加-static会报cannot find -lc,Ubuntu 需要安装 libc6-dev-static

四、高频配套查看命令

  1. file 文件名:识别文件类型(源文件 / 目标文件 / 静态链接程序 / 动态链接程序)
  2. ldd 可执行文件:查看程序依赖哪些动态共享库
  3. which gcc:查看 gcc 编译器所在路径
  4. 系统文件位置
    • 系统头文件:/usr/include/(stdio.h 等)
    • 系统库文件:/lib64/

五、头文件防止重复包含(配套)

头文件被多次#include会重复定义,两种保护写法:

cpp 复制代码
// 写法1:条件编译(兼容所有编译器)
#ifndef HEADER_NAME_H
#define HEADER_NAME_H
// 头文件内容
#endif
// 写法2:#pragma once(简洁,部分老编译器不支持)
#pragma once

六、常用 gcc 选项速查表

参数 作用
-E 只做预处理,输出.i
-S 预处理 + 编译,输出.s 汇编
-c 预处理 + 编译 + 汇编,输出.o 目标文件,不链接
-o xxx 指定输出文件名
-static 强制静态链接
-g 加入调试信息,给 gdb 调试用
-O0 / -O1 / -O2 / -O3 优化等级,O0 不优化,O3 优化最强
-Wall 开启全部警告,推荐写代码带上

七、易混易错点

  1. .o目标文件不能直接执行:缺少符号地址的重定位,必须经过链接
  2. gcc 默认动态链接,不是静态链接
  3. 头文件只做声明,函数实现放在.c 源文件;链接阶段才找函数实现
  4. 预处理只是文本替换 ,不做语法检查,语法错误在 -S 编译阶段才报错

八、最简实操串

复制代码
# 分步全套
gcc -E test.c -o test.i
gcc -S test.i -o test.s
gcc -c test.s -o test.o
gcc test.o -o test
静态链接版本
gcc test.c -o test_static -static
一键编译+开启警告
gcc -Wall test.c -o test
相关推荐
xiaoyunxuexibiji37 分钟前
供应商认证管理怎么做?从资质审核到现场认证、样品认证
运维·服务器·人工智能
JoyCong199840 分钟前
参数测评:ToDesk个人版、团队版、企业版的设备数、并发通道与安全能力对比
运维·服务器·网络·安全·智能手机·远程工作
zxanz143 分钟前
多域名、泛域名 SSL 怎么选?避开证书采购的溢价陷阱
服务器·网络协议·ssl
pirateeee1 小时前
Yocto 构建基于官方 imx6ull-14x14-evk 的 ubuntu 镜像
linux·嵌入式硬件
梦想的初衷~1 小时前
CESM2 完整实操学习大纲:环境搭建、案例运行、耦合模拟与代码开发
linux·地球系统模式·cesm2·气候模式模拟·clm 陆面模型·cam 大气模式·模式移植编译
无敌糖果1 小时前
Nginx 后端error.log疯狂刷盘问题分析
运维·nginx
a5986521 小时前
原生ARM云手机实测|ARM服务器底座,主流手游兼容性、闪退、帧率专项测试
服务器·arm开发·智能手机·云手机
a5986521 小时前
工作室批量多开云手机|ARM 服务器方案成本解析、架构选型与踩坑总结
服务器·arm开发·智能手机·云手机
逐流人1 小时前
ELK企业级日志分析系统:从零搭建集中化日志平台
运维·elk·云原生·云计算·日志分析