GCC编译器(1)
1.GCC简介
GCC(GNU Compiler Collection)是一套功能强大、性能优越的编程语言编译器,它是GNU计划的代表作品之一。GCC是Linux平台下最常用的编译器,GCC原名为GNU C Compiler,即GNU C语言编译器,随着GCC支持的语言越来越多,它的名称也逐渐变成了GNU Compiler Collection。下面对GCC的基本使用方法进行介绍。
2.基本选项
GCC编译器的基本选项如下表:
类型 | 说明 |
-E | 预处理后即停止,不进行编译、汇编及连接 |
-S | 编译后即停止,不进行汇编及连接 |
-c | 编译或汇编源文件,但不进行连接 |
-o file | 指定输出文件file |
我们都知道程序的编译要经历预处理、编译、汇编以及连接4个阶段。在预处理阶段,主要处理C语言源文件中的#ifdef、#include、以及#define等命令。在与处理过程中,GCC会忽略掉不需要预处理的输入文件,该阶段会生成中间文件*.i。
对如下的源程序example.c:
#include <stdio.h> int main() { int x; for(x=1;x<=10;x++) printf("%d\n",x); return 0; }
使用如下命令对上面的源文件进行预处理。
$ gcc -E example.c -o example.i
上面使用了两个选项:-E和-o file,其中-E表示在预处理结束后即停止编译过程;-o指定输出文件问file。前面的选项不同,输出的文件类型也不相同,可能为预处理后的C代码、汇编文件、目标文件或可执行文件,这里即为预处理后的C代码。
预处理后输出文件example.i的内容为:
# 1 "example.c" # 1 "<built-in>" # 1 "<命令行>" # 1 "example.c" # 1 "/usr/include/stdio.h" 1 3 4 ...(中间部分省略) extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__)); # 936 "/usr/include/stdio.h" 3 4 # 2 "example.c" 2 int main() { int x; for(x=1;x<=10;x++) printf("%d\n",x); return 0; }
从上面的代码可以看出,GCC对源文件所包含的头文件stdio.h进行了预处理,由于输出文件example.i比较长,上面只给出了部分内容。
在编译阶段,输入的是中间文件*.i,编译后生成的是汇编语言文件*.s。对应的GCC命令为:
$ gcc -S example.i -o example.s
example.s即为生成的汇编文件,其内容为:
.file "example.c" .section .rodata .LC0: .string "%d\n" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp movl $1, 28(%esp) jmp .L2 .L3: movl $.LC0, %eax movl 28(%esp), %edx movl %edx, 4(%esp) movl %eax, (%esp) call printf addl $1, 28(%esp) .L2: cmpl $10, 28(%esp) jle .L3 movl $0, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1" .section .note.GNU-stack,"",@progbits
上面的语句主要实现了一个for循环,有汇编基础的朋友应该很容易看懂,这里不多介绍了。
上面的例子是在预处理后C代码的基础上进行编译的,其实可以直接从源代码编译,使用的命令为:
$ gcc –s example.c –o example.o
汇编是将输入的汇编语言文件转换为目标代码,可以使用-c选项来完成。对应GCC命令为:
$ gcc –c example.s –o example.o
最后,将生成的目标文件与其他目标文件(或库文件)连接成可执行的二进制代码文件。使用的命令为:
$ gcc example.o –o example
运行example,输出结果如下:
./example
1
2
3
4
5
6
7
8
9
10
以上代码从预处理、编译、汇编以及连接一步步介绍,主要目的是讲解程序编译的整个过程以及GCC的各个选项,其实如果只需要最终的可执行文件,可以直接对源代码进行编译连接,命令如下:
$ gcc example.c –o example
对于一个程序的多个源文件进行编译连接时,可以使用如下格式:
gcc –o test first.c second.c third.c
该命令将同时编译3个源文件,将它们连接成一个可执行文件,名为test。
上面的例子都给出-o选项,如果没有给出该选项,默认的输出结果为:预处理后的C代码被送往标准输出,即输出到屏幕,汇编文件为example.s,目标文件为example.o,而可执行文件问a.out。
3.优化选项
GCC具有优化代码的功能,主要的优化选项包括如下:
- -O0:不进行优化处理。
- -O或-O1:进行基本的优化,这些优化在大多数情况下都会使程序执行得更快。
- -O2:除了完成-O1级别的优化外,还要一些额外的调整工作,如处理器指令调度等,这是GNU发布软件的默认优化级别。
- -O3:除了完成-O2级别的优化外,还进行循环的展开以及其他一些与处理器特性相关的优化工作。
- -Os:生成最小的可执行文件,主要用于在嵌入式领域。
一般来说,优化级别越高,生成可执行文件的运行速度也越快,但消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候才考虑对最终生成的代码进行优化。
- -finline-functions:允许编译器将一些简单的函数在其调用处展开。
- -funswitch-loops:将循环体中值不改变的变量移到循环体外。
具体的命令格式如下:
$ gcc –O2 –finline-functions example.c –o example
下面给出一个例子来看看GCC优化项的效果,源程序为:example6.c。
#include<stdio.h> int main () { int x; int sum=0; for(x=1;x<=le9;x++) { sum+=(x*300)/256; } return 0; }
首先不加任何优化选项,对上面的源程序进行编译如下:
$ gcc example6.c -o example6
$ time ./example6
real 0m4.750s
user 0m4.696s
sys 0m0.012s
time命令的输出结果由一下3部分组成:
- real:程序的总执行时间,包括进程的调度、切换等时间。
- user:用户进行执行的时间。
- sys:内核执行的时间。
接下来使用优化选项-O2对上面的源程序进行处理:
$ gcc -O2 example6.c -o example6^C
$ time ./example6
real 0m1.473s
user 0m1.460s
sys 0m0.008s
从上面的结果可以看出,程序的性能得到了大幅度的改善。
未完待续…