注:本文为 “GCC 编译” 相关合辑。 图片清晰度受引文原图所限。 略作重排,未整理去重。 如有内容异常,请看原文。
GCC 的使用简介与命令行参数说明
delphiwcdj 原创于 2011-06-19 20:29:00 发布
本文阐述 GCC 编译器的基础用法与拓展功能,涵盖编译配置项、告警输出、库文件调用、调试配置、交叉编译等内容,适用于学习 GCC 编译流程、规范代码编写的开发人员。
参考资料:《GNU gcc嵌入式系统开发》,作者:董文军
1. GCC 的基本用法
调用 GCC 编译器时,需要指定对应参数与待处理文件名称。不同类型编译参数的先后顺序不会改变执行结果,同类参数的顺序会影响执行逻辑。若使用多个 -L 参数指定库文件检索目录,GCC 会依照参数书写顺序依次检索对应目录。
GCC 多数参数由多字符构成,不支持单字符参数合并写法,该类参数在 Linux 系统中称作短参数。例如 -dr 与分开书写的 -d -r 具备不同语义。GCC 内置编译参数数量超过 100 个,下文仅介绍日常使用频次较高的参数。
GCC 标准调用格式:gcc [options] [filenames]
options 代表编译参数,filenames 代表待处理文件名称。常用参数说明如下:
1.1 -c
仅执行编译操作,不执行链接流程。编译器读取 .c 等源码文件,生成后缀为 .o 的目标文件。该参数多用于编译不含程序入口的子程序文件。
1.2 -o output_filename
指定输出文件名称为 output_filename,输出文件名称不可与源文件名称一致。未配置该参数时,GCC 默认生成可执行文件 a.out。
1.3 -g
生成 GNU 调试工具 gdb 所需的符号信息。对源码进行调试操作时,必须添加该参数。
1.4 -O
开启编译与链接阶段的代码优化。经过优化处理的可执行文件运行效率会提升,但编译、链接耗时会相应增加。同时优化操作会对调试工作造成干扰,可能出现运行表现与源码逻辑不一致的情况。该参数一般用于软件正式版本的编译打包。
1.5 -O2
执行强度高于 -O 的编译与链接优化,整体编译、链接流程耗时更长。
1.6 -Idirname
将 dirname 指向的目录加入头文件检索路径,该参数作用于预处理阶段。
C 语言头文件引入分为两种形式:
#include <stdio.h>
#include "stdio.h"
若项目头文件分散在多个目录中,需要多次使用 -I 参数依次配置检索路径。
1.7 -Ldirname
将 dirname 指向的目录加入库文件检索路径,该参数作用于链接阶段。默认状态下,链接程序 ld 在系统默认目录(如 /usr/lib)中检索库文件。配置该参数后,链接程序会优先检索 -L 指定目录,再检索系统默认目录。库文件存放于多个目录时,需多次使用该参数配置路径。
1.8 -lname
链接名称为 libname.a 的函数库,库文件取自系统默认目录或 -L 参数指定的目录。示例:-lm 表示链接数学函数库 libm.a。
1.9 基础使用示例
现有 C 语言源码文件 test.c,代码内容如下:
#include <stdio.h>
int main(void)
{
printf("Hello world\\n");
return 0;
}
生成可执行文件的基础指令:
gcc test.c -o test
完整编译流程分为三步:
默认配置下,预处理、编译、链接流程会一次性执行。
1.10 分步执行编译流程
GCC 支持在编译的四个阶段中断执行,对应参数如下:
分步执行指令示例:
# 预处理
gcc -E test.c -o test.i
# 编译生成目标文件
gcc -c test.c -o test.o
# 汇编生成汇编文件
gcc -S test.c -o test.s
# 链接生成可执行文件
gcc test.o -o test
1.11 多文件与库链接示例
项目包含 testmain.c、testsub.c 两个源码文件,程序调用系统数学库,编译指令如下:
gcc testmain.c testsub.c -lm -o test
其中 -lm 用于链接系统数学库 libm.a。
1.12 工程编译说明
源码数量较多的工程,单次使用一条 GCC 指令完成全量编译会造成资源损耗。若仅修改单个文件,该方式会重复编译所有未变更文件。针对该场景,可搭配 make 工具完成增量编译。
2. 告警提示配置项
GCC 集成完备的错误检测与告警输出能力,可辅助开发人员规范代码编写。
2.1 -pedantic
编译不符合 ANSI/ISO C 语言标准的源码时,输出对应告警信息。
#include <stdio.h>
void main(void)
{
long long int var = 1;
printf("It is not standard C code!/n");
}

以违规代码为例,该参数可检测出以下问题:
2.2 -Wall
GCC 多数告警类参数以 -W 为前缀,-Wall 可启用绝大多数告警检测规则,输出全面的告警信息。

编译输出的告警不属于程序错误,但会提升故障出现概率。建议编译源码时固定添加 -Wall 参数,便于排查隐性代码问题。
2.3 -Werror
将所有告警信息判定为错误,触发告警时编译流程立即终止。该参数适用于自动化编译场景,强制对存在问题的代码进行修正。

2.4 -Wcast-align
检测指针地址对齐异常。若无需地址对齐的指针指向要求地址对齐的变量,编译器会输出告警。例如 char * 类型指针指向 int * 类型地址,int 类型数据通常要求地址可被 2 或 4 整除。
2.5 其他常用参数
3. 库文件操作配置项
Linux 平台下的软件开发,通常需要调用第三方库文件。从代码开发角度,库文件由头文件(.h)与库文件(.so / .a)组成。系统默认头文件存放于 /usr/include/,库文件存放于 /usr/lib/,文件路径自定义时,需要通过编译参数配置检索规则。
3.1 -I
向头文件检索路径中新增自定义目录。
3.2 -L
向库文件检索路径中新增自定义目录,适用于库文件未存放在系统默认路径的场景。
3.3 -l
Linux 平台库文件命名遵循统一规则,文件名以 lib 作为前缀。使用 -l 参数时可省略该前缀,例如 -lfoo 会自动链接 libfoo.so 文件。
3.4 -static
Linux 库文件分为静态链接库(后缀 .a)与动态链接库(后缀 .so)。二者区别在于代码加载时机:静态库代码在编译阶段载入程序,动态库代码在程序运行阶段载入程序。
GCC 默认优先使用动态链接库,动态库缺失时才会检索静态库。添加 -static 参数可强制使用静态链接库。
3.5 -shared
生成共享目标文件,该文件可与其他目标文件链接整合为可执行文件。
4. 调试配置项
gdb(GNU Debugger)与 GCC 配合使用,可在 Linux 平台搭建完整的程序调试环境。
4.1 -g 与 -ggdb
默认编译模式下,GCC 不会向二进制文件写入调试符号信息,以此控制文件体积。需要调试时,可使用 -g 或 -ggdb 参数生成调试符号。
-g 支持分级配置调试信息粒度,格式为 -g[数字],共分为三个等级:
补充说明:
- 函数调用回溯:追踪程序运行过程中的函数调用链路;
- 堆栈转储:以十六进制格式保存程序运行现场。
调试参数会增大二进制文件体积,同时增加程序运行开销,仅建议在开发、测试阶段使用。
4.2 -p 与 -pg
向二进制文件植入性能剖析数据,用于定位程序性能瓶颈。
4.3 -save-temps
保留编译流程产生的中间文件。示例指令:
gcc test.c -o test -save-temps
执行后除可执行文件 test 外,还会保留预处理文件 test.i、汇编文件 test.s 等中间文件。
5. 交叉编译配置项
常规编译场景中,GCC 生成的目标代码适配当前运行主机的硬件架构。GCC 支持交叉编译,可生成适配其他 CPU 架构的目标代码。
嵌入式系统开发常以 X86 架构 PC 作为宿主机,借助 GCC 交叉编译功能完成嵌入式设备的程序开发。本文不展开详述具体参数配置。
GCC 的基本使用
Balaaam 原创于 2022-03-26 17:18:23 发布
1. GCC 概述
2. GCC 编译流程
完整编译分为四个阶段:预处理、编译、汇编、链接。

C 语言源码为人类可读格式,运行前需要转换为机器指令,并按照可执行文件格式打包存储至磁盘。
3. GCC 常用参数与示例

编译指令示例:
# 预处理
gcc -E hello.c -o hello.i
# 编译生成汇编文件
gcc -S hello.i -o hello.s
# 汇编生成目标文件
gcc -c hello.s -o hello.o
# 链接生成可执行文件
gcc hello.o -o hello
# 一次性完成编译与链接
gcc hello.c -o hello
# 编译生成目标文件
gcc -c hello.c
gcc -c hello.c -o hello.o
4. 多文件编译
项目文件:hello_fn.h、hello_fn.c、main.c
# 一次性编译链接
gcc hello_fn.c main.c -o newhello
# 分步独立编译
gcc -Wall -c main.c -o main.o
gcc -Wall -c hello_fn.c -o hello_fn.o
gcc -Wall main.o hello_fn.o -o newhello
5. 头文件与库文件作用
6. 系统默认检索路径
头文件路径
- /usr/include 及其子目录
- /usr/local/include 及其子目录
库文件路径
- /usr/lib
- /usr/local/lib
7. 外部库调用示例
源码文件 calc.c:
#include <math.h>
#include <stdio.h>
int main()
{
double x = pow(2.0, 3.0);
printf("The cubed if %f\\n", x);
return 0;
}
编译指令:
gcc -Wall calc.c -o calc -lm
-lm 用于链接 libm.so 或 libm.a 数学库。
8. 静态库与共享库
9. 静态库生成与使用
项目文件:hello_fn.h、hello_fn.c、main.c
# 编译生成目标文件
gcc -Wall hello_fn.c -o hello_fn.o
# 打包为静态库,ar 为 GNU 归档工具
ar rcs libhello.a hello_fn.o
# 链接静态库生成可执行文件
gcc -Wall main.c libhello.a -o main
# 通过 -L、-l 参数调用静态库
gcc -Wall -L . main.c -o main -lhello
10. 库文件检索优先级
11. 共享库生成与使用
编译参数说明
示例指令
# 生成共享库
gcc -shared -fPIC hello.o -o libhello.so
# 链接共享库
gcc main.o -o main -L . -lhello
12. 共享库运行配置
程序运行时加载共享库,可选择以下三种配置方式:
GCC 简介和命令行参数说明
KuoGavin 于 2021-05-12 14:01:15 发布
本文介绍 GCC 编译器基础用法、告警配置、库文件操作、调试配置、交叉编译,并区分 GCC 与 G++ 的使用差异。

链接阶段会完成数据段合并(合并只读数据以节省内存)与地址回填,对应符号解析、地址重定位两大流程。

内存布局说明:栈区域向低地址扩展,堆区域向高地址扩展,共享库占用中间地址空间。
1. GCC 基本用法
调用规则与前文一致,基础调用格式:
gcc [options] [filenames]
\\text{gcc [options] [filenames]}
gcc [options] [filenames]
| -c | 仅编译不链接,由 .c 源码生成 .o 目标文件,多用于子程序编译 |
| -o output_filename | 自定义输出文件名称,未配置时默认生成 a.out |
| -g | 生成 gdb 调试所需符号信息 |
| -O | 开启编译与链接优化,提升程序运行效率,增加编译耗时,干扰调试工作 |
| -O2 | 高阶代码优化,编译、链接耗时进一步增加 |
| -Idirname | 新增头文件检索目录,区分两类头文件引入规则 |
| -Ldirname | 新增库文件检索目录,链接阶段优先检索该目录 |
| -lname | 链接库文件,省略库文件前缀 lib,示例:-lm 对应 libm.a / libm.so |
编译示例
源码文件 test.c:
#include <stdio.h>
int main(void)
{
printf("Hello world\\n");
return 0;
}
基础编译指令:
gcc test.c -o test
分步编译指令:
# 预处理
gcc -E test.c -o test.i
# 汇编
gcc -S test.c -o test.s
# 编译生成目标文件
gcc -c test.c -o test.o
# 链接
gcc test.o -o test
多文件+库链接示例:
gcc testmain.c testsub.c -lm -o test
大型工程建议搭配 make 工具实现增量编译。
2. 告警提示配置项
| -v | 输出 GCC 完整执行流程 |
| –target-help | 展示支持的 CPU 架构 |
| -Q | 输出编译统计信息与函数名称 |
| -pedantic | 检测不符合 ANSI/ISO C 标准的代码并输出告警 |
| -Wall | 启用全量告警检测规则 |
| -Werror | 告警升级为错误,触发告警则终止编译 |
| -Wcast-align | 检测指针地址对齐异常 |
测试代码:
#include <stdio.h>
void main(void)
{
long long int var = 1;
printf("It is not standard C code!\\n");
}
该代码存在多处语法规范问题,对应不同参数的运行效果参考下图:
-pedantic 参数运行效果
-Wall 参数运行效果
-Werror 参数运行效果
告警信息可辅助排查隐性故障,推荐常态化使用 -Wall 参数。
3. 库文件操作配置项
| -I | 新增头文件检索目录 |
| -L | 新增库文件检索目录 |
| -l | 链接指定库文件,省略前缀 lib |
| -static | 强制使用静态链接库 |
| -shared | 生成共享目标文件 |
4. 调试配置项
| -g / -ggdb | 生成调试符号,支持三级调试信息粒度划分 |
| -p / -pg | 植入性能剖析数据,用于性能调优 |
| -save-temps | 保留编译中间文件 |
调试参数会增大文件体积与运行开销,仅用于开发调试阶段。
5. 交叉编译配置项
GCC 支持生成跨架构目标代码,嵌入式开发场景常使用该功能,具体参数不作展开。
6. GCC 与 G++ 对比
6.1 统一编译流程
6.2 支持的文件后缀
6.3 常见认知误区纠正
| GCC 仅编译 C 代码,G++ 仅编译 C++ 代码 | 二者均可编译 C/C++ 代码。后缀为 .c 的文件,GCC 按 C 语法解析,G++ 按 C++ 语法解析;后缀为 .cpp 的文件,二者均按 C++ 语法解析。编译阶段 G++ 会调用 GCC,C++ 项目通常使用 G++ 完成链接操作。 |
| GCC 不会定义 __cplusplus 宏,G++ 一定会定义 | 该宏用于标识代码解析语法。.c 文件使用 GCC 编译时,宏不生效;其余场景宏生效。 |
| 编译使用 GCC,链接必须使用 G++ | 编译可任选 GCC / G++;链接可使用 G++,或使用 gcc -lstdc++。C++ 标准库不会由 GCC 自动链接,因此 C++ 项目多选用 G++ 完成链接。 |
链接器、链接过程及相关概念解析
KuoGavin 于 2021-05-15 22:54:45 发布
链接是将代码、数据片段整合为单一文件的过程,生成的文件可加载至内存并运行。该流程由链接器执行,按照执行时机分为三类:
1. 编译器驱动程序
编译器驱动程序会按需调用预处理器、编译器、汇编器、链接器。
示例:存在 main.c、sum.c 两个文件,main.c 调用 sum.c 内函数,编译指令:
gcc -Og -o prog main.c sum.c
源文件至可执行文件的编译流程
静态链接流程
生成可执行文件 prog 后,执行指令 ./prog。Shell 识别该文件为可执行文件,调用操作系统加载器完成程序运行。
2. 目标文件
目标文件分为三类:
文件转换流程:
文本源文件
⟶
ccl & as
可重定位文件
⟶
ld
可执行目标文件
\\text{文本源文件} \\stackrel{\\text{ccl \\& as}}{\\longrightarrow} \\text{可重定位文件} \\stackrel{\\text{ld}}{\\longrightarrow} \\text{可执行目标文件}
文本源文件⟶ccl & as可重定位文件⟶ld可执行目标文件
目标模块为二进制字节序列,目标文件是存储在磁盘中的目标模块,单个目标文件可包含多个目标模块。
主流系统文件格式:Linux 与 Unix(x86-64)使用 ELF 格式,Windows 使用 PE 格式,MacOS 使用 Mach-O 格式。下文基于 ELF 格式展开说明。
2.1 可重定位目标文件(.o)
2.2 可执行目标文件
ELF 可执行文件设计为适配内存加载,程序头部表记录文件片段与内存段的映射关系。
加载器将文件内容从磁盘复制至内存,并跳转到程序入口地址执行,该过程称为加载。Linux 程序可通过 execve 函数调用加载器。
程序运行内存映像
2.3 共享目标文件(.so / .dll)
静态库存在维护成本高、内存占用大的问题,共享库可解决上述问题。共享库又称共享目标文件,支持多进程共用同一份库代码。
共享库共享特性:
共享库编译与调用
生成共享库指令:
gcc -shared -fpic -o libvector.so addvec.c multvec.c
参数说明:
- -fpic:生成位置无关代码,是共享库的必备配置;
- -shared:指定输出为共享库格式。
链接共享库生成可执行文件:
gcc -o prog21 main2.c ./libvector.so

可执行文件运行流程:
运行时动态加载接口
Linux 提供专用接口支持程序运行时加载共享库:
Java JNI 技术基于该套接口实现本地 C/C++ 函数调用。
3. 链接器的核心任务
3.1 符号解析
符号分为函数、全局变量、静态变量三类。符号解析的作用是将每一处符号引用,匹配到唯一的符号定义。
符号与符号表
每个可重定位目标文件包含符号表 .symtab,记录文件内定义与引用的符号,局部非静态变量不记录在符号表中。目标模块内的符号分为三类:
符号表包含节索引、符号类型等字段,存在三类伪节标识:
- ABS:不可重定位符号;
- UNDEF:未定义符号;
- COMMON:未初始化全局变量。
多重全局符号解析规则
按照定义形式区分强符号、弱符号:函数、已初始化全局变量为强符号;未初始化全局变量为弱符号。解析规则:
上述规则易引发隐性逻辑错误,编写代码时需规避全局符号重定义。
静态库符号解析
静态库(后缀 .a)通过 ar 工具打包生成,链接器仅复制程序实际引用的目标模块。
静态库创建指令:
ar rcs libvector.a addvec.o multvec.o
链接指令:
gcc -static -o prog2c main2.o ./libvector.a
gcc -static -o prog2c main2.o -L . -lvector
链接器维护三个集合:待合并目标文件集合 E、未解析符号集合 U、已定义符号集合 D。按照命令行顺序扫描文件:
命令行中目标文件与库文件的顺序需要匹配依赖关系。
3.2 重定位
编译器、汇编器默认从地址 0 开始生成代码与数据。符号解析完成后,链接器明确各代码段、数据段的大小,重定位环节会为所有符号分配运行时内存地址,并修改符号引用地址。
重定位条目
汇编器生成目标文件时,针对外部符号、全局变量生成重定位条目,指导链接器修改地址引用。代码段重定位条目存放于 .rel.text,数据段重定位条目存放于 .rel.data。
ELF 格式定义多种重定位类型,两类基础类型:
重定位执行步骤
4. 总结
reference
- gcc 的使用简介与命令行参数说明_gcc -l -share -fpic -g-CSDN 博客 https://blog.csdn.net/delphiwcdj/article/details/6555073
- GCC 编译器详解-CSDN 博客 https://blog.csdn.net/weixin_50697073/article/details/123759516
- gcc 简介和命令行参数说明_gcc -t-CSDN 博客 https://blog.csdn.net/yueguangmuyu/article/details/116703618
- 链接器、链接过程及相关概念解析-CSDN 博客 https://blog.csdn.net/yueguangmuyu/article/details/116710102




