1 概述
══════
GNU编译工具集(即GCC)包含一个编译器和一套将高层源码生成二进制文件的工具
集。GCC不仅是GNU/Linux上事实上的编译器,还是嵌入式系统开发的标准编译器。
这里只是简单的了解下GCC的基本特点和一些高级特点(如优化),GCC的全部功能
还需要去看官方的文档。[http://gcc.gnu.org/]
2 编译简介
══════════
阶段:预编译—>编译—>汇编—>链接各编译阶段的输入与输出:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
阶段 输入 输出 GCC示例
预编译 *.c *.i gcc -E test.c -o test.i
编译 *.i *.s gcc -S test.i -o test.s
汇编 *.s *.o gcc -c test.s -o test.o
链接 *.o gcc test.o -o test
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
在预处理阶段,源文件和包含的头文件一起处理。在这一些阶段,对#ifdef这类
的预编译指令进行解析,然后通过编译阶段的编译为汇编做准备,在汇编阶段,
文件转化为相应的机器指令,生成目标文件。最后将这些机器码链接起来(有可
能与其他机器码链接)形成可执行的二进制文件。
3 GCC的格式
═══════════
将源文件编译成可执行文件:$ gcc test.c -o test这样就生成了一个test的可
执行文件(-o 就是输出选项)。如果希望生成源文件的目标文件,可以使用-c选
项$ gcc -c test.c这样生成的目标文件默认为test.o,也可以强制要求生成其他
名字。$ gcc -c test.c -o newtest.o当有多个源文件时, 使用下面的命令能
够将三个源文件编译并链接为一个名为 goal的可执行文件$ gcc -o goal
first.c second.c third.c注意:如果只是把一个源文件编译为目标文件,那么
因为链接还没有开始,所以源文件不需要一定有main函数。
4 有用的选项
════════════
1.在很多情况下,保存头文件的目录与保存源文件的目录不是同一个。如:源程
序保存在./src中,头文件在./inc中。我们可以在./src目录中编译程序,告诉
gcc头文件在./inc中。$ gcc test.c -I ../inc -o test可以重复使用I格式来
指定包含多个子目录: $ gcc test.c -I ../inc —I ../../inc2 -o test 2.为
了配置软件,可以在编译的时候指定符号常量如在源文件或头文件中定义符号常
量:#define TEST可以在命令行中使用-D定义:$ gcc -D TEST test.c -o test
3.用于汇编而不用于源码编译阶段的选项,如:$ gcc -c -g -Wa, -ahl, -L
test.c -Wa选项将后续选项直接传递到汇编阶段供汇编器使用。
5 编译警告
══════════
最常用的检查普通警告的选项是-Wall,它打开经常遇到问题的警告。$ gcc
-Wall test.c -o test -Wall 有一个含义相同的命令:-all-warings -Wall选
项打开的警告选项
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
选项名称 用途
unused-function 无定义但有声明的静态函数时警告
unused-label 声明但未使用的符号警告
unused-parameter 声明但未使用的函数参数警告
unused-variable 未使用的本地声明的变量
unused-value 计算机得到却没有被使用的值警告
format 检验printf等,确认参数一致
implicit-int 没有规定类型的声明时警告
implicit-function-declaration 调用未经声明的函数时警告
char-subscripts 数组以char类型做下标时警告
missing-braces 聚合初始化两边缺少括号时警告
parentheses 缺少的()可能导致意义含混时警告
return-type 函数声明不指定返回值类型或指明了没有return
sequence-point 存在可疑的代码元素(如 a[i] = c[i++])
switch 对于没有default的switch语句,如果在case中未列出警告
strict-aliasing 对变量别名进行严格检查
unknown-pragmas 不可识别的#pragma指令时警告
uninitialized 使用未初始化的变量警告(仅在-o2优化下生效)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
大多数的选项都有一个否定形式,用来将他们关掉。如你打开了-Wall选项,想
要关闭unused警告集,可以输入下面的命令。$ gcc -Wall -Wno -unused
test.c -o test除了-Wall打开的警告,还有一些其他有用的警告。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
选项名称 用途
cast-align 只要有指针被强制类型转换而导致需要的地址对其边界扩展时,发出警告
sign-compare 有符号类型与无符号类型进行比较,可能导致错误的结果时,发出警告
missing-prototypes 没有预先声明函数就使用全局函数时,发出警告
Packed 结构体带有packed属性而实际上并没有用到紧缩式时,发出警告
Unreachable-code 存在根本不会被执行的代码时,发出警告
Inline 标记为inline的函数不能内联时警告
Disabled-optimization 优化器不能优化某项指定的优化时(优化时间太长或资源太多),发出警告
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
最后有一个-Werror选项,使编译器把所有的警告当做错误处理。这样编译可以
确保代码高质量。
6 GCC优化器
═══════════
实际上,优化工作有三个各不相容的方向:1.速度更快,文件更小2.速度更快,
允许文件变大3.减小文件,允许程序变慢
优化设置及其说明:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
优化等级 说明
-O0 不进行优化(默认等级)
-O, -O1 尝试同时缩短编码时间和减小映像文件
-O2 比O1更多的优化,只在不导致文件增大时进行加快速度的优化,只在不导致速度变慢时减小文件的优化
-Os 以减小结果文件为优化目标
-O3 更多的优化
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$ gcc -Os test.c -o test
6.1 -O0优化
─────
在这个等级,编译器只生成简单的代码,能够保证得到预期的结果,易于采用调
试工具进行调试,编译的速度会快很多。
6.2 -O1优化
─────
这个等级的目标是尽可能快的编译,让结果文件代码尽量小,运行速度尽量
快。-O1的各项优化选项
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
优化选项 说明
defer-pop 推迟从堆栈中弹出函数参数,知道该参数确实被使用时才弹出
thread-jumps 执行线程跳转优化(避免从一个跳转跳到另一个跳转)
branch-probabilities 采用分支整形来优化分支结构
cprop-register 采用复制生成的方式来优化寄存器之间的传值
guess-branch-probability 打开分支预测功能
omit-frame-pointer 不产生栈帧(如果可以的话)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
如果需要安全的调整结果镜像,-O1优化是一个安全的优化等级可以使用-f选项
明确指出具体的优化功能。如可以用-f-defer-pop打开该优化选项,如果要关掉,
可以使用否定格式:-fno-defer-pop
6.3 -O2优化
─────
这个优化等级包括了更多的优化(包括-O1的优化) -O2的优化
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
优化选项 说明
align-loops 对齐循环的起点
align-jumps 对齐仅通过跳转可达的标签
align-labels 对其所有标签
align-function 对其所有函数的起点
optimize-sibling-calls 优化兄弟和尾部递归调用
cse-follow-jumps 执行CSE时跟随跳转至其目标
cse-skip-blocks 执行CSE时,跟随条件跳转
gcse 执行全局公共子表达式的消除
expensive-optimizations 执行一套高成本的优化
strength-reduce 执行降低强度的优化
rerun-cse-after-loop 在循环优化后再运行CSE
caller-saves 在调用函数前保存寄存器,在返回时恢复它们
force-mem 在使用前将内存中的操作数复制到寄存器中
peepole2 在sched2之前激活一个与机器相关的深度优化rt1
regmove 打开寄存器迁移优化
strict-aliasing 认为源代码遵守了严格命名规则
delete-null-pointer-checks 删除无用的空指针检查
reorder-blocks 对基本块进行重排序以改善代码的布局
schedule-insns 对寄存器分配之前重新安排指令
schedule-insns2 在寄存器分配之后重新安排指令
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6.4 -Os优化
─────
这个等级就是-O2的优化,除去那些可能增大结果映像的选项-Os关闭的选项
为:-falign-loops, -falign-jumps, -falign-labels, -falign-functions。
6.5 -O3优化
─────
这个等级在O2的优化上新增了优化
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
优化选项 说明
-finline-functions 把简单函数内联到调用函数中
-frename-registers 为拥有大量寄存器的架构优化寄存器的分配(这将加大调试的难度)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7 架构相关优化
══════════════
-mcpu选项告诉编译器生成针对特定CPU类型的指令。
8 调试选项
══════════
如果想用符号调试器来调试代码,可以用-g来指定在映像中为GDB调试信息。-g
可以带一个参数,用来指定调试信息的格式。如想要生成dwarf-2的调试信息,
可以使用下面的命令:$ gcc -gdwarf-2 test.c -o test
9 其他工具
══════════
9.1 size工具
──────
size工具给出text大小(指令数),data段,bss段
9.2 objdump工具
───────
能够得到有关映像的更详尽的细节,使用–syms参数可以得到符号列表$ objdump
–syms test.o使用–disassemble参数可以反汇编文件
9.3 mm工具
─────
有助于理解在目标文件中出现的符号
有关GCC的更全面的介绍,请看官方文档:http://gcc.gnu.org/
有关GNU/Linux的介绍:https://www.gnu.org/