天天看点

从编译原理来看引用类型和值类型

 看了好多书,研究了下值类型和引用类型,总是有种模棱两可的感觉,这让我萌发了,大众角度主要是分析内存中的东西,而我准备从内存和编译原理两个方面来入手,本人先说明我没学过编译原理,但略读了下,发现用这个来解释更好。参考《程序设计编译原理》这本书,我不知道VS IDE的编译工具是采用什么编译原理的,但大体的猜了下,应该和这个差不多。

每个编译器都有各自的符号表,用来存储所有标识符(变量名,常量名等)

我是根据C语言来思考C#当中的两种类型

c语言源程序到.exe可执行程序的转换需要经过好多步骤。其中就有编译一步。而在编译这一步,有细分为好几小步。我记得不是很清楚,好想有什么词法分析,语法分析,名称表生成,代码优化,等等等等。   

在编译过程中,当系统发现你定义了一个变量并给它赋了初值,就会给该变量分配相应的存储空间,并把该变量的名称和存储空间的第一个字节的地址存在一个变量名表里。由该变量的类型可以知道该变量占据的存储空间大小。所有的变量都一样。   

以后要使用某一个变量的时候,便会到变量名表里查该变量的名字,要是没找到,表示没有定义该变量,提示错误;要是找到了,就会知道该变量的存储空间地址了,如是就到该存储空间里把变量的值取出来。   

这就是使用变量的一个过程。其中生成变量名表的过程是在编译的过程形成的。   

所以说:使用变量,就是通过变量名,访问变量存储空间里的值,也就是变量的值。

变量分为变量名和变量值

注意这里只看变量名,变量名是存储在栈符号表中的,符号表说白了就是一个文件,而这个文件也占内存,在内存中是用栈来保存其中的信息的

名称 相关信息 相对地址
变量

类型(所有值类型和引用类型)

种属(简单变量,类,结构,数组等)也就是隶属于什么

长度(所需的存储单元数)

若为数组 记录其内情向量

其它的不怎么重要就不写了

值类型的相对地址:

随着变量的定义,同时生成自己的存储单元,相对地址也就是自己的地址 如 int a,同时建立a存储单元,这些都是在内存中的堆栈产生的。然后赋值给a存储单元,则相对地址就是a存储单元的这个地址。

引用类型的相对地址:

不会随着变量定义而同时生成自己的存储单元,所以与符号表中对应的地址是你赋值给它的那个地址,默认是null,也就是相对地址为null 如 Form f=new form(); 这个f是个变量名,存储在变量名表中,变量声明的时候没有自动生成自己的存储单元,也就是存储变量值的地方,所以它是用来引用一个存储单元的,在栈中存储它是引用地址,并在编译的时候将它写入变量名表中的相对地址,这里就是new Form()在堆中的内存地址,注意new分配空间是在堆中分配空间的地址

关于Form f=null的讨论

 1、曾使用过f,然后才让f   =   null;的话。在堆里面不占空间。而在栈里面,还是要占用一些空间的。这个空间原来存放的是对原有的对象的引用(reference),现在为NULL了。  

  2、未使用过a,直接写a   =   null;这样的话,在栈里面也要占空间的。堆里不占用。  

值类型的 在变量名表当中的相对地址,当声明变量的时候,已经固定不变了,栈中存储单元是存储赋给的值,相对地址是栈中存储单元的地址。

引用类型 在变量名表当中的相对地址,会不段随着引用的不同,而不段发生变化,栈中存储的引用地址

关键的地方:

右值和左值(适用于值类型和引用类型):

  • 右值根据相对地址,计算取其对应的存储单元来使用,也就是对变量的使用是取其对应的存储单元里的值,最后代表的是这个存储单元里的值 如 b=a 中的 a就是在进行右值操作 ,发生在使用变量的时候
  • 左值根据相对地址,计算取其对应的存储单元来使用,用来赋值给这个存储单元 如 b=a中的 b就是在进行左值操作,发生在赋值给变量的时候