GCC(GNU C Compile)是Linux下标准的C编译器,能够编译C,C++ ,Object C 等多种语言,并且GCC支持夸平台编译,即在当前的CPU平台为不同的硬件平台和体系结构开发软件。(以下本文中所用GCC 版本为4.8.2,ubuntu14.02)
(1)安装GCC
sudo apt-get install gcc
安装后查看gcc版本命令: gcc -v
(2)程序编译原理
GCC编译器的命令格式:
gcc [options] [filenames]
为了更加深刻的记忆gcc的命令,了解程序的编译原理是有必要的。对于GCC编译器来说,程序编译需要经过以下四个阶段:
预处理(Pre-Processing)
编译(Compiling)
汇编(Assemling)
连接(Linking)
下面详细介绍这四个阶段:
- 预处理(Pre-Processing)
预处理阶段是由GCC的预处理器(CPP)完成,其作用是将宏的求值,条件编译以及其他需要将代码传递到编译器前的工作,通常所见的 # 号后面的语句由CPP来进行处理。
执行:
gcc -o hello.i -E hello.c
以下为hello.i文件的部分内容:
# 1 "hello.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 374 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 385 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 386 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 375 "/usr/include/features.h" 2 3 4
# 398 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 399 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4
....
# 2 "hello.c" 2
int main()
{
printf("Hello! This is our embedded world!n");
return 0;
}
由此可见,CPP将stdio.h文件中的内容插入到hello.i文件中,即预处理。
编译
GCC在编译阶段会进行如下工作:检查代码的规范性以及语法的错误,确定代码实际要完成的任务,在确定无误后将C语言翻译成汇编语言,且并不将汇编语言转化为二进制代码。在此阶段,程序若出现错误,此种错误被称为编译错误。
执行:
gcc -o hello.s -S hello.i
以下为hello.a的部分内容:
.file "hello.c"
.section .rodata
.LC0:
.string "hello world! "
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
汇编(Assembling)
汇编阶段是把编译生成的”.s”文件转成目标二进制代码。
执行
gcc -o hello.o -c hello.s
连接
在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。
读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明, 而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了, 在没有特别指定时,Gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函 数”printf”了,而这也就是链接的作用。
函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要 库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这 样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。Gcc在编译时默认使用动态库。