软件开发工具——GCC


GCC(GNU Compiler Collection,GNU编译套装)能够编译C、C++等语言。原本只能处理C语言,但是随着众多开发者的加入和GCC自身的发展,如今的GCC已成为可编译多种语言的编译器,如C/C++,Java,Fortran,Pascal等,GCC也由原来的GNU C Compiler变为GNU Compiler Collection,能够在当前CPU计算机平台上,为众多不同体系的硬件平台开发软件服务,尤其应用于嵌入式开发领域。

目前Linux默认使用的C编译器是GCC,具有如下优点:

  • 方便进行编译控制

  通过GCC能够完美地控制整个编译过程,用户可以根据需要,在任何阶段让编译终止或暂停,以检查或使用编译器在该阶段的输出信息,并最终生成二进制文件。

  • 提供灵活强大的代码优化功能

  通常使用"-On"控制优化配置,n越大,代码优化越明显。

  • 与GDB调试工具完美结合

  GDB调试工具具有查看程序运行状态、设置断点、查看表达式、显示变量等众多功能。GCC和GDB的结合使用是开发者进行C语言编程的利剑。

  • 人性化编译选项

  提供众多的编译选项可以定制人性化的编译过程。例如可以产生调试信息、优化代码执行过程、选取需要的头文件等。

  • 充足的提示信息

  提供很多警告信息,增强了程序的稳定性和可移植性。

GCC编译过程分为四个阶段:预处理、编译、汇编、链接。如下图所示:

linux c语言开发redis linux开发c语言用什么软件_java

linux c语言开发redis linux开发c语言用什么软件_操作系统_02

提供帮助:



gcc --heip



GCC常用选项:

选项 

含义

-V

查看gcc编译器的版本,显示gcc执行时的详细过程

 -O file

把输出文件制定到file中,该选项可以输出汇编文件、目标文件以及可执行文件。

 -E

只对源文件进行预处理,不做编译、汇编及链接,GCC会忽略任何不需要预处理的输入文件。 

 -S

只进行编译,不做汇编及链接,对于每个输入的非汇编语言文件,输出文件都是汇编语言文件。 

 -c

只进行汇编,不做链接,汇编成源文件的目标文件,默认状态下生成.o文件,GCC忽略-c选项后面任何无法识别的输入文件。 

例如新建文件hello.c如下:



#include <stdio.h>

int main()
{
  printf("hello, world\n");
}



 

第一步 预处理阶段

执行命令: gcc -o hello.i -E hello.c

预处理器cpp根据以字符开头#开头的命令,修改原始C程序。比如hello.c中的第一行为 #include <stdio.h>,预处理器便将stdio.h的内容直接插入到程序中。预处理之后得到文本文件hello.i,打开如下:



# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 1 3 4
# 33 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 424 "/usr/include/features.h" 3 4
.........
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 868 "/usr/include/stdio.h" 3 4 
   
  
# 2 "hello.c" 2
 
   
  
# 3 "hello.c"
int main()
{
 printf("hello, world\n");
}



预处理就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些代码输出到一个“.i”文件中等待进一步处理。

第二步 编译阶段

执行命令: gcc -o hello.s -S hello.i

编译器ccl将文本文件hello.i 翻译为hello.s,这个文件里面包含一个汇编程序,就是把C/C++代码(比如上面的".i"文件)“翻译”成汇编代码。汇编语言是非常有用的,因为它将不同高级语言的不同编译器提供了通用的输出语言。如下所示:



.file    "hello.c"
    .text
    .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
    leaq    .LC0(%rip), %rdi
    call    puts@PLT
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size    main, .-main
    .ident    "GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
    .section    .note.GNU-stack,"",@progbits



 

第三步 汇编阶段

执行命令:gcc -c -o hello.o hello.s

汇编器as将hello.s翻译成机器语言保存在hello.o中。汇编就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现位ELF目标文件(OBJ文件)。

第四步 链接阶段

执行命令: gcc -o hello hello.o

链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。链接分为静态链接和动态链接两种。

动态链接:动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。 动态链接生成的程序体积较小,但是必须依赖所需的动态库,否则无法执行。

静态链接:静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过静态链接生成的程序体积较大。

结果得到hello 可执行文件,可以被加载到内存中由系统执行。

./hello

完整的执行过程为:



keegen@keegensCP:~/test$ touch hello.c
keegen@keegensCP:~/test$ vim hello.c
keegen@keegensCP:~/test$ gcc -o hello.i -E hello.c
keegen@keegensCP:~/test$ ls -a
.  ..  hello.c  hello.i
keegen@keegensCP:~/test$ gcc -o hello.s -S hello.i
keegen@keegensCP:~/test$ ls -a
.  ..  hello.c  hello.i  hello.s
keegen@keegensCP:~/test$ gcc -c -o hello.o hello.s
keegen@keegensCP:~/test$ ls
hello.c  hello.i  hello.o  hello.s
keegen@keegensCP:~/test$ gcc -o hello hello.o
keegen@keegensCP:~/test$ ls
hello  hello.c  hello.i  hello.o  hello.s
keegen@keegensCP:~/test$ ./hello
hello, world



总结编译器的编译过程:

源文件-->预处理-->编译/优化-->汇编-->链接 -->可执行文件

在编译过程中。除非使用了"-c",“-S”,或"-E"选项(或者编译错误阻止了完整的过程),否则统一完整链接步骤。如运行



gcc hello.c 或 gcc -o hello hello.c



keegen@keegensCP:~/test$ rm -rf hello hello.i hello.o hello.s
keegen@keegensCP:~/test$ ls
hello.c
keegen@keegensCP:~/test$ gcc hello.c
keegen@keegensCP:~/test$ ls
a.out  hello.c
keegen@keegensCP:~/test$ ./a.out
hello, world
keegen@keegensCP:~/test$ gcc -o hello hello.c
keegen@keegensCP:~/test$ ls
a.out  hello  hello.c
keegen@keegensCP:~/test$ ./hello
hello, world