1. GCC 快速入门
Gcc 命令的常用格式是: Gcc [选项] 要编译的文件 [选项] [目标文件]
其中,目标文件可以默认,Gcc默认生成的可执行文件名是:a.out
我们来看看经典的入门程序“HelloWorld!”
#vihello.c
#包括
#包括
无效主(无效)
printf("你好世界!rn");
用gcc编译来执行程序。
#gcchello.c
该命令会直接从hello.c生成最后的二进制补码可执行程序a.out
该命令意味着执行(1)预处理,(2)汇编,(3)编译和(4)链接以生成最终的二进制补码可执行程序。 这里没有指定输出文件,默认输出为a.out。
如何指定最后的二进制补码可执行程序名称,因此使用-o选项来指定名称。 例如需要生成执行程序hello.exe
所以
#gcchello.c - ohello.exe
二、GCC命令分析——四步
从前面我们知道GCC编译源代码生成最终可执行的二进制补码程序,GCC后台包括四个阶段的步骤。
GCC编译C源代码分四步:
预处理----->编译---->汇编---->链接
现在我们利用GCC命令选项来一一分析GCC的流程。
1)预处理
此阶段,编译器编译出C源代码中包含的stdio.h等头文件,用户可以使用gcc选项“-E”查看。
用法:#gcc-Ehello.c-ohello.i
功能:预处理hello.c并输出hello.i文件。
[root]#gcc-Ehello.c-ohello.i
[根]#ls
你好.chello.i
[root]#vihello.i
#1“你好.c”
#1""
#1""
#1“你好.c”
#1“/usr/include/stdlib.h”13
#25 “/usr/include/stdlib.h”3
#1“/usr/include/features.h”13
#291 “/usr/include/features.h”3
#1“/usr/include/sys/cdefs.h”13
#292 “/usr/include/features.h”23
#314 “/usr/include/features.h”3
#1“/usr/include/gnu/stubs.h”13
#315“/usr/include/features.h”23
#26“/usr/include/stdlib.h”23
#3“你好.c”2
无效主(无效)
printf("你好世界!rn");
2)编译阶段(Compiling)
第二步是编译阶段。 在这个阶段,Gcc首先需要检测代码的规范性,是否存在语句错误等,以确定代码中实际要做的工作。 检测正确后,Gcc将代码翻译成汇编语言。 用户可以使用“-S”选项查看,该选项只编译不汇编,生成汇编代码。
选项
用法:[root]#gcc –Shell.i –ohello.s
功能:将预处理输出文件hello.i编译成hello.s文件。
[root@richardhello-gcc]#ls
你好.chello.ihello.s
hello.s的汇编代码如下
[root@richardhello-gcc]#vihello.s
.文件“你好.c”
.section.rodata
.LC0:
.string“你好世界!rn”
。文本
.globlmain
.typemain,@function
主要的:
推入%ebp
movl%esp,%ebp
低于 8 美元,%esp
andl$-16, %esp
movl$0,%eax
subl%eax,%esp
低于 12 美元,%esp
Pushl$.LC0
调用printf
addl $16,%esp
movl$0,%eax
离开
雷特
.Lfe1:
.sizemain、.Lfe1-main
.ident“GCC:(GNU)3.2.220030222(RedHatLinux3.2.2-5)”
3)组装阶段(Assembling)
汇编阶段是将编译阶段生成的“.s”文件转换为二补码目标代码。
选项-c
用法:[root]#gcc –chello.s –ohello.o
功能:编译汇编输出文件test.s,输出test.o文件。
[root]#gcc-chello.s-ohello.o
[根]#ls
你好.chello.ihello.ohello.s
4)链接阶段(Link)
编译成功后,进入链接阶段。
没有选项链接
用法:[root]#gcchello.o – ohello.exe
功能:将编译后的输出文件hello.o链接成最终的可执行文件hello.exe。
[根]#ls
你好.chello.exehello.ihello.ohello.s
运行可执行文件gcc源码如何编译,正确结果如下。
[root@localhostGcc]#./你好
你好世界!
这里涉及到一个重要的概念:函数库。
读者可以回顾一下这个小程序。 在这个程序中,没有定义“printf”的函数实现gcc源码如何编译,而预编译中包含的“stdio.h”只有这个函数的声明,但没有定义该函数的实现。 那么,“printf”函数是在哪里实现的呢? 最终的答案是:系统将这个函数实现到一个名为libc.so.6的库文件中。 如果没有特殊说明,gcc会到系统默认的搜索路径“/usr/lib”去搜索,即链接到libc.so.6库函数,这样就可以实现“printf”这个函数了,这就是链接的功能。
可以使用ldd命令查看动态库加载状态:
[root]#lddhello.exe
libc.so.6=>/lib/tls/libc.so.6(0x42000000)
/lib/ld-linux.so.2=>/lib/ld-linux.so.2(0x40000000)
函数库通常分为静态库和动态库。 静态库是指在编译链接时,将库文件的所有代码都添加到可执行文件中,因此生成的文件比较大,但运行时不再需要库文件。 它的后缀通常是“.a”。 相反,动态库在编译链接时并不将库文件的代码添加到可执行文件中,而是在程序执行时从运行时链接文件中加载库,这样可以节省系统开支。 动态库通常带有“.so”后缀。 如上所述,libc.so.6是一个动态库。 gcc编译时默认使用动态库。