C语言编译过程总结

        开发C程序有四个步骤:预处理、编译、汇编和链接。任何一个体系结构处理器上都可以使用C语言程序,只要该体系结构处理器有相应的C语言编译器和库,那么C源代码就可以编译并连接到目标二进制文件上运行。

C语言编译过程总结_第1张图片

我们创建一个test.c为例来讲解程序编译的过程

test.c:

#include 

int add(int a, int b)
{
    return a+b;
}
int sub(int a, int b)
{
    return a-b;
}

int main()
{
    int a = 88;
    int b = 66;

    int sum = add(a, b); 
    printf("a=%d, b=%d, a+b=%d\n", a, b, sum);

    int dif = sub(a, b); 
    printf("a=%d, b=%d, a-b=%d\n", a, b, dif);
}

一:预处理

        使用预处理器把源文件test.c经过预处理生成test.i文件,预处理用于将所有的#include头文件以及宏定义替换成其真正的内容。预处理主要工作如下:

  • 处理所有的注释,以空格代替
  • 将所有的 #define 删除,并且展开所有的宏定义
  • 处理条件编译指令 #if, #ifdef, #elif,#else,#endif
  • 处理 #include,展开被包含的文件
  • 保留编译器需要使用的 #pragma 指令

预编译命令:

gcc -E test.c -o test.i

        上述命令中-E是让编译器在预处理之后就退出,不进行后续编译过程;-o是指定输出文件名。预处理之后得到的仍然是文本文件。-I指定头文件目录,这里指定的是我们自定义的头文件目录。

        test.i文件的部分截图如下:

C语言编译过程总结_第2张图片

二:编译

对预处理文件进行词法分析,语法分析和语义分析

  • 词法分析:分析关键字,标示符,立即数等是否合法
  • 语法分析:分析表达式是否遵循语法规则
  • 语义分析:在语法分析的基础上进一步分析表达式是否合法

分析结束后进行代码优化生成相应的汇编代码文件

编译命令:

gcc -S test.c -o test.s

生成的汇编文件如下:

C语言编译过程总结_第3张图片

[root@localhost testEg]# cat test.s
        .file   "test.c"
        .text
        .globl  add
        .type   add, @function
add:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    -8(%rbp), %eax
        movl    -4(%rbp), %edx
        addl    %edx, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   add, .-add
        .globl  sub
        .type   sub, @function
sub:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    -8(%rbp), %eax
        movl    -4(%rbp), %edx
        subl    %eax, %edx
        movl    %edx, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1:
        .size   sub, .-sub
        .section        .rodata
.LC0:
        .string "a=%d, b=%d, a+b=%d\n"
.LC1:
        .string "a=%d, b=%d, a-b=%d\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB2:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    $88, -4(%rbp)
        movl    $66, -8(%rbp)
        movl    -8(%rbp), %edx
        movl    -4(%rbp), %eax
        movl    %edx, %esi
        movl    %eax, %edi
        call    add
        movl    %eax, -12(%rbp)
        movl    -12(%rbp), %ecx
        movl    -8(%rbp), %edx
        movl    -4(%rbp), %eax
        movl    %eax, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    -8(%rbp), %edx
        movl    -4(%rbp), %eax
        movl    %edx, %esi
        movl    %eax, %edi
        call    sub
        movl    %eax, -16(%rbp)
        movl    -16(%rbp), %ecx
        movl    -8(%rbp), %edx
        movl    -4(%rbp), %eax
        movl    %eax, %esi
        movl    $.LC1, %edi
        movl    $0, %eax
        call    printf
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE2:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)"
        .section        .note.GNU-stack,"",@progbits

三:汇编

  • 汇编器将汇编代码转变为机器的可以执行指令
  • 每条汇编语句几乎都对应一条机器指令

 这一步产生的文件叫做目标文件,是二进制格式

gcc汇编过程通过as命令完成:

as test.s -o test.o

 等价于:

gcc -c test.s -o test.o

四:链接

        链接过程使用链接器将该目标文件与其他目标文件、库文件、启动文件等链接起来生成可执行文件。

链接命令如下:

gcc test.o -o test

最后运行生成的可执行文件:

[root@localhost testEg]# ./test
a=88, b=66, a+b=154
a=88, b=66, a-b=22

总结:

编译过程分为预处理,编译,汇编和链接四个阶段

  • 预处理:处理注释,宏以及已经以 # 开头的符号
  • 编译:进行词法分析,语法分析和语义分析等
  • 汇编:将汇编代码翻译为机器指令的目标文件
  • 链接:链接到一起生成可执行程序

常用编译命令选项

假设源程序文件名为test.c。

1. 无选项编译链接
用法:#gcc test.c
作用:将test.c预处理、汇编、编译并链接形成可执行文件。这里未指定输出文件,默认输出为a.out。

2. 选项 -o
用法:#gcc test.c -o test
作用:将test.c预处理、汇编、编译并链接形成可执行文件test。-o选项用来指定输出文件的文件名。

3. 选项 -E
用法:#gcc -E test.c -o test.i
作用:将test.c预处理输出test.i文件。

4. 选项 -S
用法:#gcc -S test.i 
作用:将预处理输出文件test.i汇编成test.s文件。

5. 选项 -c
用法:#gcc -c test.s
作用:将汇编输出文件test.s编译输出test.o文件。

6. 无选项链接
用法:#gcc test.o -o test
作用:将编译输出文件test.o链接成最终可执行文件test。

7. 选项-O
用法:#gcc -O1 test.c -o test
作用:使用编译优化级别1编译程序。级别为1~3,级别越大优化效果越好,但编译时间越长。

你可能感兴趣的:(C/C++,c语言)