重温编译原理(1)

编码完成之后,不论它能否正确的达到我们预期的目标,首先都需要通过编译器的编译,也就是说我们的编码至少是在语法上是正确的。所谓语法的正确就是我们的代码要符合所用语言的语法规定,其实质是使得编译器能够正确的解析,从而得到目标代码在机器上执行。   

有时候,通过编译仅仅是一个开始,我们会发现程序的结果同我们的预想往往有较大差异。抛开算法本身的问题不说,一些编程细节的疏忽导致的问题足以让我们百思不得其解,着急、郁闷却有无可奈何。例如,参数传递问题,变量作用域问题,返回局部变量等等问题,一直在困扰着我们。即使在网上看到过一些相关文章讲解堆、栈等等知识,但仍然经常犯糊涂,貌似其竟是如此神秘。

如果我们能够知道编译器是如何工作的,那么对这些问题的认识就会上升一个层次,许多困扰我们的不解与疑问或许就会迎刃而解,豁然开朗。因此,我们不妨抽出一段时间,重温一下似乎早已遗忘的编译原理教材吧!

编译器的内存分配策略

假定编译器从操作系统得到一块存储区,用于被编译的程序运行。该存储区将被分块,用来保存:

1.  产生的目标代码

2.  静态数据

3.  记录程序调用的控制栈

4. 

程序每次执行时所需的信息都用一块连续的存储区来管理,称作活动记录。其中包括了以下信息域:返回值,实参,控制链(可选),访问链(可选),保存的机器状态,局部数据和临时数据。

其中,返回值保存被调用程序返回给调用程序的值;实参域保存调用函数传给被调用函数的参数值;控制链指向调用者的活动纪录;访问链用来应用其他活动记录中的非局部数据;激起状态域保存程序调用前机器的状态信息,如:程序计数器的值和各种寄存器的值等等;局部数据域保存局部于程序执行的数据;临时数据保存如计算表达式的中间结果这样的临时数据。

编译器一般有三种内存分配策略:

1.  静态分配

2.  栈式分配

3.  堆式分配

静态分配,变量在编译时就确定了存储空间,静态变量的值被绑定到固定的存储空间,因此每次函数调用后局部于函数的静态变量仍然绑定到同样的存储单元,即使调用完成,局部变量的值仍然保存。

栈式分配,把存储空间组织为栈,而且随着程序调用的开始和结束进行进栈和出栈。每次调用都将一个活动记录压入堆栈,调用结束时弹栈。堆式分配,把连续的存储区分成块,当活动记录或其他对象需要时就分配。块的释放可以按照任意顺序进行。注意,这部分不是由编译器自动释放的,必须由程序员来处理。

了解了编译器的内存分配方式,我们就可以理解一些经常遇到的问题的原因。

返回局部变量的引用

过程每次调用时,局部变量的存储空间都包含在每次调用的活动纪录中。由于每次调用都引起新的活动记录进栈,所以每次调用时,局部变量都分配到不同的存储单元。而且在活动记录弹栈的时候所有局部变量的存储空间将被释放,保存的值将被删除。

参数传递(按值传递,按指针传递,按引用传递)

参数传递方法之间的区别主要是基于实参代表的是左值、右值还是实参的正文本身(待续)

 

你可能感兴趣的:(编译器,存储,活动,算法,编程,语言)