XPG:X/Open 可移植性指南。X/Open 可移植性指南(由 X/Open Company, Ltd.出版), 是比 POSIX 更为一般的标准。X/Open 拥有 Unix 的版权,而 XPG 则指定成为 Unix 操作系统必须满足的要求。
---------------------------------------
GCC是GNU CC的简称,它是符合上述ANSI C标准的编译系统,能够编译C、C++、Object C等语言编写的程序。GCC还是一个交叉平台编译器,能够在当前CPU平台为多种不同架构的硬件平台开发软件,因此适合嵌入式领域的开发编译。
GCC所支持的后缀名的解释
.c C原始程序
.C/.cc/.cxx C++原始程序
.m Object C原始程序
.i 已经过预处理的C原始程序
.ii 已经过预处理的C++原始程序
.s/.S 汇编语言原始程序
.h 预处理文件(头文件)
.o 目标文件
.a/.so 编译后的库文件
三、详解GCC编译流程
使用vi编写源文件hello.c。
#include
int main()
{
int i;
for(i=1;i<9;i++)
printf("Hello World %d times!\n",i);
return 0;
}
1、预处理阶段
该阶段的作用是把预处理文件,也就是头文件编译进来。在此例中,就是要把stdio.h编译进来。可使用-E选项查看,作用是让gcc在预处理结束后停止编译过程。
[armlinux@lqm program]$ gcc -E hello.c -o hello.i
[armlinux@lqm program]$ cat hello.i | less
# 1 "hello.c"
# 1 ""
# 1 ""
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3
# 28 "/usr/include/stdio.h" 3
# 1 "/usr/include/features.h" 1 3
# 291 "/usr/include/features.h" 3
# 1 "/usr/include/sys/cdefs.h" 1 3
# 292 "/usr/include/features.h" 2 3
# 314 "/usr/include/features.h" 3
# 1 "/usr/include/gnu/stubs.h" 1 3
# 315 "/usr/include/features.h" 2 3
# 29 "/usr/include/stdio.h" 2 3
...
extern void funlockfile (FILE *__stream) ;
# 679 "/usr/include/stdio.h" 3
# 2 "hello.c" 2
int main()
{
int i;
for(i=1;i<9;i++)
printf("Hello World %d times!\n",i);
return 0;
}
由此可见,stdio.h的内容已经插入到hello.c中,即GCC完成了预处理过程。
2、编译阶段
GCC的工作是首先检查代码的规范性、是否有语法错误,以确定代码实际要做的工作。检查无误后,将之翻译为汇编语言。可用-S来查看,即只编译而不进入汇编阶段。
[armlinux@lqm program]$ gcc -S hello.i -o hello.s
[armlinux@lqm program]$ cat hello.s
.file "hello.c"
.section .rodata
.LC0:
.string "Hello World %d times!\n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
movl $1, -4(%ebp)
.L2:
cmpl $8, -4(%ebp)
jle .L5
jmp .L3
.L5:
subl $8, %esp
pushl -4(%ebp)
pushl $.LC0
call printf
addl $16, %esp
leal -4(%ebp), %eax
incl (%eax)
jmp .L2
.L3:
movl $0, %eax
leave
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
3、汇编阶段
GCC把编译生成的.s文件转换为目标文件.o。这时使用-c选项就可以看到汇编代码已经转换成.o的目标代码了。
[armlinux@lqm program]$ gcc -c hello.s -o hello.o
4、链接阶段
成功编译之后,就进入了链接阶段。这里首先要明白“库”的概念。这个程序中没有“printf”的函数实现,且在预处理阶段包含进来的“stdio.h”
中只有该函数的声明,而没有定义函数的实现。如何实现“printf”?答案是:系统把这些函数的实现都做到名位libc.so.6的库文件里了,没有特
别指定时,GCC会到默认的搜索路径“/usr/lib”下进行查找。也就是链接到libc.so.6库函数中去来实现函数“printf”,这就是链接
的作用。
在RedHat 9下的函数库如下:
/lib:系统必备共享库
/usr/lib:标准共享库和静态库
/usr/X11R6/lib:X11R6的函数库
/usr/local/lib:本地函数库
头文件:
/usr/include:系统头文件
/usr/local/include:本地头文件
在/etc/ld.so.conf中包含着共享库的搜索位置。我的libc.so.6在/lib下面,它是一个link,指向libc-2.3.2.so。这是因为我用的C库--glibc版本是2.3.2。
函数库一般可分为静态库和动态库两种。静态库是指链接时把库文件的代码全部加到可执行文件中,因此生成的文件比较大,但是在运行时就不再需要库文件了。其
后缀名一般为“.a”。动态库在链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态
库一般后缀名为“.so”。GCC在编译时默认使用动态库。
[armlinux@lqm program]$ gcc hello.o -o hello1
[armlinux@lqm program]$ ls -l hello1
-rwxrwxr-x 1 armlinux armlinux 11582 8月 28 17:42 hello1
[armlinux@lqm program]$ file hello1
hello1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not sd
[armlinux@lqm program]$ gcc -static hello.o -o hello2
[armlinux@lqm program]$ ls -l hello2
-rwxrwxr-x 1 armlinux armlinux 423442 8月 28 17:43 hello2
[armlinux@lqm program]$ file hello2
hello2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, statically linked, not stripped
[armlinux@lqm program]$ ./hello1
Hello World 1 times!
Hello World 2 times!
Hello World 3 times!
Hello World 4 times!
Hello World 5 times!
Hello World 6 times!
Hello World 7 times!
Hello World 8 times!
[armlinux@lqm program]$ ./hello2
Hello World 1 times!
Hello World 2 times!
Hello World 3 times!
Hello World 4 times!
Hello World 5 times!
Hello World 6 times!
Hello World 7 times!
Hello World 8 times!
hello2是静态编译,大小423442,是动态编译的36.56倍。利用file可以看出此文件statically linked,还是dynamically linked (uses shared libs)。
至此,GCC的整个编译链接过程就完成了。