C语言的编译和执行过程

Page content

没有系统的学过C语言,为了了解java编译流程。
先简单的略过了一下C语言的编译过程。

C语言在Linux系统下编译是有4个过程

  1. 预处理Pre-processing
  2. 编译Compilation
  3. 汇编Assemble
  4. 链接Linking

如下图所示每个过程会生成不同的文件。
这些文件会被RAM读取loading后,在系统中运行。

图片备用地址
c_compile


1.预处理Pre-processing

简单的说预处理就是处理#开头的命令。

  • 删除注释: 去掉没必要的注释。
  • 宏定义指令: 如#difine, #undef。(替换这些值)
  • 头文件包含指令: 如#include。(直接插入include的代码)
  • 条件编译指令: 如#ifdef,#ifndef,#else,#elif,#endif等。
  • 特殊符号: 如在源程序中出现的#line标识将被解释为当前行号。

下面简单的写代码看一下过程。

#include <stdio.h>
#define A 10
#define B 20
int main(){
        int a=A;
        int b=B;
        int c=a+b;
        printf("%d + %d = %d\n",a,b,c);
}

创建first.i文件

$ gcc -E first.c -o first.i

查看一下first.i内容

$ cat first.i
# 1 "first.c"
# 1 "<built-in>" 1
... ...
int main(){
        int a=10;
        int b=20;
        int c=a+b;
        printf("%d + %d = %d\n",a,b,c);
}

可以看出A的值被替换了, stdio.h的内容也插入进来了。


2.编译Compilation

做完预处理后,Compiler会做编译成Assemble。
编译的过程可以分3个阶段。Front-end,Middle-end,Back-end。
Source code -> (GENERIC -> GIMPLE) -> (SSA -> RTL) -> Assembly)

图片备用地址
c_compile

2.1 Front-end

第一阶段会执行以下内容。

  • 词法分析: 把C语言代码分成最小单位(token)。
  • 语法分析: 用token创建Parse Tree检查语法错误。
  • 语义分析: 之后检查整体语义有没有问题。比如用错变量,数据类型不一致等等…
  • 中间代码生成: 生成Compile Tree。
2.2 Middle-end

这个阶段会把Compile Tree转换成SSA(Static Single Assignment)形态,
基于SSA做一些跟目标语言和结构无关的优化。
(例如,内联,常数传播,尾调用消除,冗余消除等,不会受限于CPU结构共同做的优化)。
基于SSA的优化还可以分局部优化,全局优化和循环优化。
最终生成高级语言和汇编的中间态的RTL(Register Transfer Language)。

2.3 Back-end

RTL通过RTL Optimizer再一次同时进行跟目标语言和结构无关的优化 + 相关的优化。
这次优化根据所在的结构和环境进一步做一些有效的优化。
这样最终生成汇编Assemble。

下面看看实际过程。

$ gcc -S first.i -o first.s

查看一下first.s内容

$ cat first.s
	.section	__TEXT,__text,regular,pure_instructions
	.macosx_version_min 10, 13
	.globl	_main                   ## -- Begin function main
	.p2align	4, 0x90
_main:                                  ## @main
	.cfi_startproc
## %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	subq	$16, %rsp
	leaq	L_.str(%rip), %rdi
	movl	$10, -4(%rbp)
	movl	$20, -8(%rbp)
	movl	-4(%rbp), %eax
	addl	-8(%rbp), %eax
	movl	%eax, -12(%rbp)
	movl	-4(%rbp), %esi
	movl	-8(%rbp), %edx
	movl	-12(%rbp), %ecx
	movb	$0, %al
	callq	_printf
	xorl	%ecx, %ecx
	movl	%eax, -16(%rbp)         ## 4-byte Spill
	movl	%ecx, %eax
	addq	$16, %rsp
	popq	%rbp
	retq
	.cfi_endproc
                                        ## -- End function
	.section	__TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
	.asciz	"%d + %d = %d\n"


.subsections_via_symbols

不知道里面写的是什么内容…ㅠㅠ


3.汇编Assemble

编译完的代码,通过汇编过程编译成机器语言。
汇编的主要目的是把目标对象 ***.o 转换成有Instruction和Data的 ELF Binary format结构。

下面看看实际过程。

$ gcc -c first.s -o first.o

查看一下first.o内容

$ cat first.o
���� �� �__text__TEXTC ��__cstring__TEXTCc__compact_unwind__LDX x�__eh_frame__TEXTx@�
                                                                                     h$
�
 PUH��H��H�=4�E�
�E��E�E�E�u�U�M��1ɉE��H��]�%d + %d = %d
CzRx
2- �$h�������CA�C
  _main_printf%

用hexdump命令查看会是什么内容?

$ hexdump -C first.o
00000000  cf fa ed fe 07 00 00 01  03 00 00 00 01 00 00 00  |................|
00000010  04 00 00 00 00 02 00 00  00 20 00 00 00 00 00 00  |......... ......|
00000020  19 00 00 00 88 01 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  b8 00 00 00 00 00 00 00  20 02 00 00 00 00 00 00  |........ .......|
00000050  b8 00 00 00 00 00 00 00  07 00 00 00 07 00 00 00  |................|
00000060  04 00 00 00 00 00 00 00  5f 5f 74 65 78 74 00 00  |........__text..|
00000070  00 00 00 00 00 00 00 00  5f 5f 54 45 58 54 00 00  |........__TEXT..|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  43 00 00 00 00 00 00 00  20 02 00 00 04 00 00 00  |C....... .......|
000000a0  d8 02 00 00 02 00 00 00  00 04 00 80 00 00 00 00  |................|
000000b0  00 00 00 00 00 00 00 00  5f 5f 63 73 74 72 69 6e  |........__cstrin|
000000c0  67 00 00 00 00 00 00 00  5f 5f 54 45 58 54 00 00  |g.......__TEXT..|
000000d0  00 00 00 00 00 00 00 00  43 00 00 00 00 00 00 00  |........C.......|
000000e0  0e 00 00 00 00 00 00 00  63 02 00 00 00 00 00 00  |........c.......|
000000f0  00 00 00 00 00 00 00 00  02 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  5f 5f 63 6f 6d 70 61 63  |........__compac|
00000110  74 5f 75 6e 77 69 6e 64  5f 5f 4c 44 00 00 00 00  |t_unwind__LD....|
00000120  00 00 00 00 00 00 00 00  58 00 00 00 00 00 00 00  |........X.......|
00000130  20 00 00 00 00 00 00 00  78 02 00 00 03 00 00 00  | .......x.......|
00000140  e8 02 00 00 01 00 00 00  00 00 00 02 00 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  5f 5f 65 68 5f 66 72 61  |........__eh_fra|
00000160  6d 65 00 00 00 00 00 00  5f 5f 54 45 58 54 00 00  |me......__TEXT..|
00000170  00 00 00 00 00 00 00 00  78 00 00 00 00 00 00 00  |........x.......|
00000180  40 00 00 00 00 00 00 00  98 02 00 00 03 00 00 00  |@...............|
00000190  00 00 00 00 00 00 00 00  0b 00 00 68 00 00 00 00  |...........h....|
000001a0  00 00 00 00 00 00 00 00  24 00 00 00 10 00 00 00  |........$.......|
000001b0  00 0d 0a 00 00 00 00 00  02 00 00 00 18 00 00 00  |................|
000001c0  f0 02 00 00 02 00 00 00  10 03 00 00 10 00 00 00  |................|
000001d0  0b 00 00 00 50 00 00 00  00 00 00 00 00 00 00 00  |....P...........|
000001e0  00 00 00 00 01 00 00 00  01 00 00 00 01 00 00 00  |................|
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000220  55 48 89 e5 48 83 ec 10  48 8d 3d 34 00 00 00 c7  |UH..H...H.=4....|
00000230  45 fc 0a 00 00 00 c7 45  f8 14 00 00 00 8b 45 fc  |E......E......E.|
00000240  03 45 f8 89 45 f4 8b 75  fc 8b 55 f8 8b 4d f4 b0  |.E..E..u..U..M..|
00000250  00 e8 00 00 00 00 31 c9  89 45 f0 89 c8 48 83 c4  |......1..E...H..|
00000260  10 5d c3 25 64 20 2b 20  25 64 20 3d 20 25 64 0a  |.].%d + %d = %d.|
00000270  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000280  43 00 00 00 00 00 00 01  00 00 00 00 00 00 00 00  |C...............|
00000290  00 00 00 00 00 00 00 00  14 00 00 00 00 00 00 00  |................|
000002a0  01 7a 52 00 01 78 10 01  10 0c 07 08 90 01 00 00  |.zR..x..........|
000002b0  24 00 00 00 1c 00 00 00  68 ff ff ff ff ff ff ff  |$.......h.......|
000002c0  43 00 00 00 00 00 00 00  00 41 0e 10 86 02 43 0d  |C........A....C.|
000002d0  06 00 00 00 00 00 00 00  32 00 00 00 01 00 00 2d  |........2......-|
000002e0  0b 00 00 00 02 00 00 15  00 00 00 00 01 00 00 06  |................|
000002f0  01 00 00 00 0f 01 00 00  00 00 00 00 00 00 00 00  |................|
00000300  07 00 00 00 01 00 00 00  00 00 00 00 00 00 00 00  |................|
00000310  00 5f 6d 61 69 6e 00 5f  70 72 69 6e 74 66 00 00  |._main._printf..|
00000320

嘿嘿,更看不懂…ㅠㅠ
只能推测出这是机器能看懂的机器语言吧?


4.链接Linking

通过汇编过程生成的机器语言在这个过程,链接(link)那些分别生成的包和程序。
printf()函数或 scanf()函数是已经有了已编号好标准的包。
不需要再次编译,在这次过程会跟程序链接(link)。

看看实际执行结果

$ gcc first.o -o first

最终结果的执行。

$ ./first
10 + 20 = 30

总结

C语言编译时会经过预处理,编译,汇编,链接4个过程
预处理时会将处理含有#符号的代码(注释,命令语以及特殊符号)
编译时会检查语法以及做一些优化
汇编时把代码转成机器可以看懂的机器语言
链接时把相关的包和程序链接起来,生成可执行文件。


欢迎大家的意见和交流

email: li_mingxie@163.com