编译原理之构建编译的相关科学

来源:互联网 发布:手机淘宝 找不到摇一摇 编辑:程序博客网 时间:2024/05/21 06:22

编译器设计和实现中的建模

研究时主要涉及正确的数学模型和正确算法之的研究
设计和选择时,需要考虑能用性和功能的要求及简单性和有效性之间的平衡

数学模型:有穷新状态机和正则表达式(主要用于描述程序中的词法单位)

最基本模型:上下文无关法,用于描述语法结构 和控制结构。


程序设计语言基础

静态和动态的区别

如果一个策略支持编译器静态决定某个问题。

静态策略:编译时刻决定

动态策略:运行时刻决定 

如public static int x.在编译时刻就决定了x的内存地址。之后所有的引用都将指向这个x。


环境与状态

环境:从一名字到存储位置的映射。

状态:一个内存位置到他们的值的映射 。


名字到位置的静态和动态绑定:静态:如C语言的全局变量。  动态:方法内的变量声明
全局变量会在编译器生成目标代码时一劳永逸的分配一个存储位置。

位置到值的静态和动态绑定:一般来说都是动态的。只有C语言有一个例外:#define ARRAYSIZE  1000

静态作用域和块结构

作用域:C语言是根据程序结构决定的。而java这种面向对象的语言则使用public、private和protected来控制 

块结构:现在的语言都是使用{}来控制,python是根据缩进来控制。还有Algol语言使用begin和end方法来控制 

动态作用域

如果一个作用域策略依赖于一个或多个只有在程序执行时刻才能知道的因素,那么它就是动态的。

术语:对一个名字x的使用指向的最近被调用但还没有终止且声明了x的过程中的这个声明。

举个栗子
#define a(x+1)int x=2;void b() {int x=1;printf("%d\n",a);}void c(){printf("%d\n",a);}void main(){b();c();}
为了解析x,必须检查所有当前活跃的函数调用,然后选择最近调用的且具有一个对x的声明的函数


第二种情况是面向对象时的方法调用问题
  1. 有一个类A,他有一个方法M();
  2. B是A的子类,且重载了M();
  3. 当有一个x.M()的方法调用的时候,x是类A的一个对象。

那么在编译的的时刻是不能指出x指向类A的对象还是其子类B的对象,只有到了运行时刻才可能决定应当调用m的哪个定义。因此编译器生成的代码必须决定对象的类,并调用其中的某一个名字的方法M。


值调用

术语:

实在参数:在调用过程中使用的参数

形式参数:在过程定义中使用的参数 


被调用过程所做的所有有关形式的计算都局限于这个过程,相应的实在参数本身不会改变
比如
int m(int x){x++;}void main(){int x=0;printf("%d",m(x));printf("%d",x);}


这种情况下x本身的值是不会改变的。只是在m方法中返回的值是x加1,实际上的运算只是对应x的一个复制品。


引用调用

实在参数的址址作为相应的参数的值被传递给被调用者

在被调用者的代码中使用形式参数时,实现方法是汕头这个指针找到调用者指明的内存位置。因此改变形式参数 看起来就像是改变了实在参数一样。


当形式参数是一个大型的对象、数组或结构时,引用调用几乎是必不可少的,原因是严格的调用要求调用者把整个实在参数拷贝到属于相应形式参数的空间上,当参数 很大时,这种拷贝可能代价高昂。


像java解决数组、字符串、和其他对象的参数传递问题的方法仅仅复制这些对象的引用,结果是java运行时就好像它对所有不是基本类型的参数都使用了引用调用。

名调用

这种机制已不再采用,所以只是简单介绍一下.

它要求被调用者的运行方式好像是用实在参数 以字面方式替换了被调用者的代码中的形式参数 一样。

当实在参数是一个表达式而不是一个变量时,会发生一些和直觉不符的问题,这也是今天不采用这种机制的原因之一。



































0 0
原创粉丝点击