编译系统GCC

4.1 GCC
在 Linux 平台上,最流行的编译系统是 GCC(GNU Compile Collection)。GCC 也是 GNU
发布的最著名的软件之一。GCC 的功能非常强大,主要体现在两方面。
1) GCC 可以为 x86、ARM、MIPS 等不同体系结构的硬件平台编译程序。
2) GCC 可以编译 C、C++、Pascal、Java 等数十种高级语言。
GCC 的这两项特性对嵌入式应用开发及其重要。此外,GCC 的编译效率也是非常高的,
一般要高出其他编译系统 20%到 30%左右。所以在嵌入式 Linux 开发领域,使用的基本上就
是 GCC 编译系统。
在红旗 Linux 6.0 系统中已经集成了 GCC,通过命令“gcc -v” 可以查看 GCC 中 C 编
译器 gcc 的详细信息。
[root@localhost ~]#gcc -v
\Using built-in specs.
Target: x86-pc-linux
Configured with: ../configure –prefix=/usr –mandir=/usr/share/man
–infodir=/usr/share/info –enable-shared –enable-threads=posix
–enable-checking=release –with-system-zlib –enable-__cxa_atexit
–disable-libunwind-exceptions –enable-libgcj-multifile –disable-libmudflap
–enable-languages=c,c++,java,fortran –disable-libgcj –with-cpu=generic
–host=x86-pc-linux
Thread model: posix
gcc version 4.2.1
gcc 命令的使用格式为:
gcc [选项] [文件名] [选项] [文件名]
gcc 命令拥有数量庞大的编译选项,按类型可以把选项分为以下几大类。
 总体选项:用于控制编译的整个流程。
常用选项:
-c:对源文件进行编译或汇编。
-E:对源文件进行预处理。
-S:对源文件进行编译。
-o file:输出目标文件 file。
-v:显示编译阶段的命令。
 语言选项:用于支持各种版本的 C 语言程序。
常用选项:
-ansi:支持符合 ANSI 标准的 C 程序。
 警告选项:用于控制编译过程中产生的各种警告信息。
常用选项:
-W:屏蔽所有的警告信息。
-Wall:显示所有类型的警告信息。
-Werror:出现任何警告信息就停止编译。
71
 调试选项:用于控制调试信息。
常用选项:
-g:产生调试信息。
 优化选项:用于对目标文件进行优化。
常用选项:
-O1:对目标文件的性能进行优化。
-O2:在-O1 的基础上进一步优化,提高目标文件的运行性能。
-O3:在-O2 的基础上进一步优化,支持函数集成优化。
-O0:不进行优化。
 连接器选项:用于控制链接过程。
常用选项:
-static:使用静态链接。
-llibrary:链接 library 函数库文件。
-L dir:指定连接器的搜索目录 dir。
-shared:生成共享文件。
 目录选项:用于指定编译器的文件搜索目录。
常用选项:
-Idir:指定头文件的搜索目录 dir。
-Ldir:指定搜索目录 dir。
此外,还有配置选项等其他选项,这里不做介绍了。
编译系统本身是一种相当复杂的程序,编写甚至读懂这样的程序都是非常困难的。但
是从事嵌入式 Linux 应用的开发人员都应掌握编译系统的基本原理和工作流程。
4.1.1 GCC 工作流程
在 C 程序的编译过程中,依次要进行预处理、编译、汇编、链接四个阶段。这里通过编
译 C 文件 test.c 来展示 GCC 的工作流程。
例如:
 test.c

include

820 “/usr/include/stdio.h” 3 4

extern void flockfile (FILE *__stream) attribute ((nothrow));
extern int ftrylockfile (FILE *__stream) attribute ((nothrow)) ;
extern void funlockfile (FILE *__stream) attribute ((nothrow));

850 “/usr/include/stdio.h” 3 4

2 “test.c” 2

int main()
{
printf(“Hello world!\n”);
return 0;
}
2. 编译阶段
编译阶段是整个编译过程中最复杂的一个阶段。这里拿自然语言的翻译过程作个对比。
比如在把“I love China”翻译成中文前,需要依次完成以下几个步骤:
1)考察这个句子中每个单词的拼写是不是正确。
2)考察整个句子的语法(比如主谓宾、定状补的结构等)是不是正确。
3)考察整个句子的语义是不是正确。
只有以上三个步骤都正常通过了,才能保证句子被正确翻译。同样,高级编程语言的编
译阶段也必须实现这三个步骤。
1) 步骤 1 称为词法分析,主要负责检查关键字、标识符等是否正确。
2) 步骤 2 称为语法分析,主要负责检查程序中语句的语法是否正确。
3) 步骤 3 称为语义分析,主要负责检查程序中语句的逻辑意义是否正确。
在 shell 中输入命令“gcc -S test.i -o test.s”。其中,参数 S 告诉 gcc 命令只进行
编译,不做其他处理。命令运行完毕后就会产生一个名为 test.s 的汇编文件。如下所示:
[root@localhost home]#gcc -S test.i -o test.s
[root@localhost home]#ls
test.c test.i test.s
在学习使用汇编语言编程的时候,对照 C 文件和其汇编程序是很好的办法。如下所示的
是 test.s 的代码。
.file “test.c”
.section .rodata
.LC0:
.string “Hello world!”
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
73
andl 16,pushl4(pushlmovlpushlsubl 4, %esp
movl .LC0,(callputsmovl 0, %eax
addl $4, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident “GCC: (GNU) 4.2.1”
.section .note.GNU-stack,”“,@progbits
注意,以上所示的汇编代码是针对 x86 平台的。
3. 汇编阶段
汇编阶段的任务是把汇编程序翻译成 CPU 可以识别的二进制文件,该文件又称为目标
文件。
在 shell 中输入命令“gcc -c test.s -o test.o”,其中,参数 c 告诉 gcc 命令只进
行汇编,不做其他处理。命令运行完毕后就会产生一个名为 test.o 的目标文件。如下所示:
[root@localhost home]#gcc -c test.s -o test.o
[root@localhost home]#ls
test.c test.i test.o test.s
在 Windows 系统中,目标文件的后缀是 obj。
4. 链接阶段
目标文件虽然已经可以被 CPU 直接识别,但是单个目标文件一般是无法运行的。原因
在于一个程序往往是由多个源文件组成的,每个源文件只对应一个目标文件。也许有人会问,
test 程序不就只有一个源文件 test.c 吗,为什么也不能直接运行呢?原因是 test.c 使用
了 stdio.h 对应的库函数,所以必须要把 test.o 文件和函数库文件链接在一起才能运行。
链接阶段的任务就是把程序中所有的目标文件和所需的库文件都链接在一起,最终生
成一个可以直接运行的文件,称为可执行文件。
在 shell 中输入命令“gcc test.o -o test”,运行完毕后就会产生一个名为 test 的
可执行文件。输入命令“./test”执行该文件,就可以得到 test 文件的运行结果“Hello
world!”。 如下所示:
[root@localhost home]#gcc test.o -o test
[root@localhost home]#./test
Hello world!
gcc 命令生成的可执行文件的有以下三种格式。
1)a.out(Assembler and Link editor output);
2)COFF(Common object file format);
3)ELF(Executable and linkable format);
74
其中,a.out 和 COFF 格式都是比较老的格式,现在 Linux 平台上可执行文件的主流格
式是 ELF。
4.1.2 Glibc
从逻辑功能上看,程序的主体是由一系列函数组成的。所以说编写程序的主要工作之一
就是实现函数。为了有效降低编程的工作量,编程系统会把一些非常基本、常用的函数集中
到函数库中实现。比如信息的打印函数、文件的打开或关闭函数、内存空间的申请与释放函
数、数学计算函数等。当程序需要使用到函数库中的某个函数时,就可以直接从库中调用。
就好比建造房屋,建筑队并不需要从头开始制造砖瓦和水泥,而只需要从原材料市场购买就
可以了。
每种高级编程语言都有各自的函数库。比如 C 语言的 C 库、Visual C++的 MFC、Java
的 JFC 等。函数库中的函数都是由经验丰富的资深程序员编写的,具有出色的运行性能和工
作效率。所以说,函数库的使用不光减少了编程的工作量,还能有效提高程序的性能和健壮
性。在面向对象语言中,函数被封装在类中,所以函数库就演变成了类库,但其原理和机制
是类似的。
函数库的使用方式分为静态链接和动态链接两种。静态链接是指编译系统在链接阶段把
程序的目标文件和所需的函数库文件链接在一起,这样生成的可执行文件就可以在没有函数
库的情况下运行。就好比火箭把燃料和氧料装在一起,就可以在没有空气的太空中飞行。动
态链接是指编译系统在链接阶段并不把目标文件和函数库文件链接在一起,而是等到程序在
运行过程中需要使用时才链接函数库。
使用静态链接方式产生的可执行文件体积较大,但运行效率较高。而使用动态链接方式
产生的可执行文件由于没有库文件,所以体积较小。但由于需要动态加载函数库,所以运行
效率要低一点。
在具体应用时,如果有多个源文件都需要调用函数库时,那么应该选择动态链接的方式。
而只有少数源文件需要调用函数库时,应该选择静态链接的方式。可以被静态链接的函数库
称为静态库,可以被动态链接的函数库称为动态库,或者共享库。
Glibc(GNU Library C)是 GNU 推出的 C 语言函数库。Glibc 符合 ISO C (International
Standard for the C programming language)和 POSIX(Portable Operating System
Interface for Computer Environments)标准。其中,ISO C 定义了 C 函式库的标准格式,
POSIX 定义了不同计算平台应该遵守的 C 函数库标准,是 ISO C 标准的扩充。因此 Glibc 可
以在各种不同体系结构的计算平台上使用。
Glibc 中包含了大量的函数库,其中 libc 是最基本的函数库,每个 C 程序都需要使用
libc 库。此外,常用的还有数学库 libm、加密库 libcrypt、POSIX 线程库 libpthread、网
络服务库 libnsl、IEEE 浮点运算库 libieee 等。Glibc 库为 C 程序提供了大量功能强大的
函数,包括输入输出函数、字符串处理函数、数学函数、中断处理函数、错误处理函数、日
期时间函数等等。
C 程序在调用 Glibc 中的函数库时,需要引用与函数库对应的头文件。比如 stdio.h、
string.h、time.h 等。这些头文件都存放在/usr/include 目录下。同时,在编译命令中需
要加入某些函数库的链接参数(在函数库的使用文档中会列出具体的链接库名称参数),并
使用符号“-l”进行连接。比如 libm 库的链接参数为 m,libpthread 库的链接参数为 pthread
等。
例如:
 test.c:

include

include

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值