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在编译时默认使用动态库。