编程语言
首页 > 编程语言> > 面经笔记本 语言基础理论 【二:程序的编译与链接】

面经笔记本 语言基础理论 【二:程序的编译与链接】

作者:互联网

目录

一、翻译环境与运行环境

二、编译与链接

2.1 大体介绍

2.2 预编译阶段

2.3 编译阶段

2.4 汇编阶段

2.5 链接

2.6 运行环境

三、宏

3.1 宏与函数的对比

3.2 宏与内联函数的对比

3.2.1 内联函数

3.2.2 宏


一、翻译环境与运行环境

在ANSI C的任何一种实现中,存在两个不同的环境:翻译环境执行环境。

注:ANSI C是美国国家标准协会对C语言发布的标准

翻译环境:

.c文件经过编译加链接,成为.exe的可执行文件。

在这个环境中,源代码被转换为可执行的机器指令;

执行环境:

用于实际执行代码。

二、编译与链接

2.1 大体介绍

组成一个程序的每个源文件会先通过编译,成为目标代码。

此时每个源文件的后缀名为.obj

每个目标文件再由链接器捆绑在一起,这个阶段叫做链接

形成了一个单一而完整的可执行程序。

后缀为.exe。

链接器还会引入标准C函数库或个人程序库中,任何被该程序所用到的函数。

编译本身也分三个阶段:

预编译-编译-汇编

2.2 预编译阶段

将.c文件进行预编译,转换成.i文件。

这个阶段进行预处理指令,

比如:

1.注释删除

2.把define定义的符号换成值

这个阶段做的是文本操作。

2.3 编译阶段

把C代码翻译成汇编代码。

我们会进行语法分析,词法分析,语义分析,最后进行符号汇总。

2.4 汇编阶段

把汇编代码转换成二进制代码,

形成.o文件

.o文件里放的已经是二进制的指令了。

整个编译完成后,每个源文件都会生成一个.obj文件,

然后进行链接,形成.exe的可执行文件。

2.5 链接

链接阶段做两件事:

1.合并段表

2.符号表的合并和重定位

2.6 运行环境

程序执行的过程:

1.程序必须载入内存中。(一般由操作系统来完成)

2.程序的执行开始,首先调用main函数

3.开始执行程序代码,这个时候程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。程序还可以使用静态内存,这里面的变量在程序的整个执行过程中一直保留他们的值。

3.终止程序。

三、宏

3.1 宏与函数的对比

1.代码长度

每次使用时,宏代码会被插入到程序中。

而函数代码只出现于同一个地方。

 

2.执行速度

宏更快,因为函数存在调用和返回的额外开销。

 

3.宏是类型无关的。

 

4.宏不方便调试。

 

5.宏无法递归。

3.2 宏与内联函数的对比

3.2.1 内联函数

引入内联函数,是为了解决程序中函数调用的效率问题。

程序在编译器编译的时候,编译器会将程序中出现的内联函数调用表达式,用内联函数的函数体进行替换。

而普通函数,并不是在编译的时候被替换,而是在运行的时候被替换。

这其实是一个以空间代价换取时间代价的节省,

所以内联函数一般都是10行以内的小函数。

在声明内联函数时,我们要使用inline关键字。

 

内联函数有两点注意:

1.不允许使用分支与循环

2.内联函数的定义必须出现在内联函数第一次调用前

 

优点:当函数体比较小时,内联函数效率高;

缺点:滥用内联会导致程序变慢。

 

3.2.2 宏

预处理器用复制宏代码的方式代替函数调用,省去了参数压栈、生成汇编语言的CALL调用、返回参数、执行return等过程,从而提高了速度。

 

内联函数和宏的区别:

内联函数和宏类似的一点在于,内联函数和宏都是在程序运行前,整个编译阶段,进行代码的替换。

区别在于,内联函数是通过编译器来替代的,而宏是通过预处理器来替代的。

而且,传统的宏定义可能会引起一些麻烦,

举个例子:

#define SQUARE(x) x*x

这是一个将参数平方的函数,

我们来运用一下:

int a = 5;

cout<<SQUARE(a+1);

乍一看,我们会以为是36,

但其实,宏只是一个单纯的替换,

在这里,经过预处理器的替换,SQUARE(a+1)会变成 a+1*a+1,也就是11.

并不符合我们的逻辑。

所以我们应该这样写:

#define SQUARE(x) (x) * (x)

而且,宏是没有参数类型检查的,这会产生安全问题。

但是,如果我们使用内联函数,就不会有这样的问题出现。

内联函数可以得到宏的替换效能,还可以得到常规函数的类型检查。

标签:函数,面经,程序,编译,3.2,内联,基础理论,链接
来源: https://blog.csdn.net/Kukeoo/article/details/117791124