C/C++编译过程

1. 编译型语言和解释型语言

如我们所知,计算机只能接受二进制代码,这种用0/1表示的二进制代码被成为机器指令。在众多编程语言中,汇编语言与机器指令相对应,它因直接操作计算机硬件而具有很高的运行效率。

对开发者更加友好的是如C/C++、java和python等高级语言。但由于高级语言是无法直接被计算机理解的,因此需要把高级语言翻译为机器语言

将高级语言翻译为低级语言一般可分为两类:编译解释

编译型语言在程序执行前有一个专门的编译过程,这个过程将程序编译为计算机可执行的二进制文件,每次运行不需要进行重新编译。但这种方式的跨平台型较差。(c/c++属于编译型语言
解释型语言不是直接将程序语言翻译为机器语言,而是先翻译成中间代码,在运行的过程中由解释器对中间代码解释运行。解释性语言每次运行时都需要进行解释,因此效率相对较低。解释性语言的跨平台性较好。(java、python属于解释型语言

2.C/C++编译过程

C/C++的编译过程可分为如下四步:

预编译

预编译过程的主要任务是处理源代码中的预编译指令(以"#"开头),例如#include、#define、#ifdefine等。
具体任务如下:

  • 删除所有的#define,并且展开所有的宏定义。
  • 处理所有的条件预编译指令,例如#ifdef#elif#else#endif等。
  • 处理所有的#include指令,将被包含的文件插入到该指令的位置。
  • 删除所有的注释///* */
  • 添加行号和文件名标识,比如#2 “hello.c” 2, 以便于调试和编译器追踪调试信息。
  • 保留所有的#pragma编译器指令。

预编译的命令如下:

gcc -E hello.c -o hello.i
或者
cpp hello.c > hello.i

将如下源代码进行预编译:

#include <stdio.h>
int main(){
	printf("Hello World!");//print msg
	return 0;
}

在终端下输入命令gcc -E hello.c -o hello.i后得到 hello.i文件。hello.i文件中的内容就是hello.c文件经过上述预编译过程之后的结果。由于hello.i文件内容过长,下面给出部分内容:

# 1 "hello.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "hello.c"
	...
	...
	...
extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
# 912 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));



extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;


extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 942 "/usr/include/stdio.h" 3 4

# 3 "hello.c" 2


# 4 "hello.c"
int main(){
 printf("Hello World!");
 return 0;
}

可以看出,hello.c文件中的注释//print msg已经被删除,且#inclued <stdio.h>中的stdio.h文件被插入到该指令的位置。

编译

编译的过程是把预编译完成之后的文件进行词法分析语法分析语义分析以及优化等操作,然后生成相应的汇编代码文件。
使用如下命令可将生成的hello.i文件进行编译生成汇编代码文件:

gcc -S hello.i -o hello.s

利用gcc也可以直接将hello.c文件直接进行预编译和编译生成汇编代码文件:

gcc -S hello.c -o hello.s

在终端中输入 gcc -S hello.c -o hello.s中得到的汇编代码文件如下:

	.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
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
	.section	.note.GNU-stack,"",@progbits

汇编

汇编过程是将汇编代码转变为计算机可以执行的机器指令。汇编的过程比较简单,只需要将指令逐条翻译为机器指令即可。
下面的gcc命令可以将hello.s文件进行汇编得到hello.o文件(即目标文件)。

gcc -c hello.s -o hello.o

或者利用gcc直接对hello.c文件进行预编译、编译、汇编三个步骤得到hello.o文件。

gcc -c hello.c -o hello.o

在终端中输入gcc -c hello.c -o hello.o得到hello.o目标文件。
由于经过汇编的文件已经是二进制文件,我们无法识别出含义,这里就不给出文件内容了,有兴趣的可以试试看。

链接

经过预编译、编译、汇编后得到的hello.o文件实际上是一个无法直接运行的中间目标文件,简称目标文件。
链接是一个很复杂的过程,要得到可执行文件需要将许多文件链接起来。它的主要目的是正确处理各个模块之间的引用,使得各个模块之间能够正确的衔接。链接过程主要包括了地址和空间分配、符号决议和重定位等步骤。
下面的gcc指令将hello.o文件进行链接,得到可执行文件:

gcc hello.o -o hello

或者直接将hello.c文件进行预编译、编译、汇编、链接得到可执行文件:

gcc hello.c -o hello

在终端中输入gcc hello.c -o hello得到可执行文件,然后输入./hello执行程序。

WavenZ@pc:~$ cd Desktop
WavenZ@pc:~/Desktop$ gcc hello.c -o hello
WavenZ@pc:~/Desktop$ ./hello
Hello World!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值