【概念】堆、栈、引用类型、值类型

一、数据类型

(1)值类型
1、概念
值类型变量可以直接分配给它一个值。
值类型直接包含数据。系统分配内存来存储值。

eg:int、char、float、long
enum、struct

2、存储
值类型总是分配在它声明的地方。
做为局部变量时,存储在栈上
做为类对象的字段时,则跟随此类存储在堆中。

(2)引用类型
1、概念
引用类型不包含存储在变量中的实际数据,但它们存储对象引用
引用类型的值(对象)是引用类型的一个实例。

换句话说,它们指的是一个内存位置。

使用多个变量时,引用类型可以指向一个内存位置。
如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。

类、接口、委托类型、数组都属于引用类型
eg:object、string、interface、int[]、Hashtable

delegate、class

2、存储
引用类型存储在堆中
类型实例化的时候,会在堆中开辟一部分空间存储类的实例。
类对象的引用存储在栈中


(3)区别
1、在给引用类型的变量赋值的时候,只是赋值了对象的引用。当我们使用引用类型时,实际上只是在处理该类型的指针。而非引用类型本身;

对于引用类型,两个变量可能引用同一个对象,因此对一个变量的操作可能影响到另一个变量所引用的对象。


2、对于值类型,每个变量都有自己的数据副本,对于一个变量的操作不可能影响到另一个变量。

 

1 Student s = new Student();
2 s.age = 10;
3 Student s2 = s;//给引用类型的变量赋值的时候,其实只是赋值了对象的引用;
4 
5 
6 int a = 0;
7 int a2 = a;//给值类型变量赋值的时候是创建了一个副本


(3)指针类型

 

二、堆、栈

(1)概念
1、堆

在c里叫堆,在c#里叫托管堆。
托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理。
当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。

2、栈
是一种抽象数据结构,它拥有以下特性:

1)只能存堆栈的顶端存取数据数据;
2)满足"后进先出的原则"。

(其实就如同餐盘向餐桌上一个一个叠放,使用餐盘时需要从最顶端往下拿,这就是典型堆栈概念的应用)

(2)数据结构堆栈
1、堆
堆是无序的,是一片不连续的内存域,如果用户自己不释放的话,当内存达到一定的特定值时,通过垃圾回收器(GC)来回收。

2、栈
保持着先进后出的原则,是一片连续的内存域,有系统自动分配和维护。


(3)内存堆栈
存在内存中的两个存储区:

1、栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放。
栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义。

2、堆区:存放着引用类型的对象,由CLR释放
堆是程序运行期间动态分配的内存空间,可以根据程序的运行情况确定要分配的堆内存的大小。


上文提及的栈(Stack),在程序运行的时候,每个线程(Thread)都会维护一个自己的专属线程堆栈。
当一个方法被调用的时候,主线程开始在所属程序集的元数据中,查找被调用方法。
然后通过JIT即时编译并把结果(一般是本地CPU指令)放在栈顶。
CPU通过总线从栈顶取指令,驱动程序以执行下去。

 

来源:

https://www.runoob.com/csharp/csharp-data-types.html

https://www.cnblogs.com/codingsilence/archive/2011/02/28/2146584.html

 

三、深拷贝和浅拷贝

1、浅拷贝

是指将对象中的所有字段逐字复制到一个新对象。

对值类型字段只是简单的拷贝一个副本到目标对象,改变目标对象中值类型字段的值不会反映到原始对象中,因为拷贝的是副本。实现浅拷贝需要使用Object类的MemberwiseClose方法用于创建一个浅表副本。

对引用类型字段则是指拷贝他的一个引用到目标对象,改变目标对象中引用类型字段的值会将它反映到原始对象中,因为拷贝的是指向堆是上的一个地址。

 

2、深拷贝

深拷贝与浅拷贝不同的是对于引用字段的处理,深拷贝会将在新对象中创建一个新的对象和原始对象字段相同(内容相同)的字段,也就是说这个引用和原始对象引用是不同,我们改变新对象这中这个字段的时候是不会影响到原始对象中对应字段的内容。

须实现ICloneable接口中的Clone方法,且需要将被克隆对象加上[Serializable]特性。

 

3、引用类型的赋值、浅拷贝和深拷贝的区别

(1)赋值

就是将原来对象的地址给新的对象拷贝一下即可。

 

(2)浅拷贝

需要继承ICloneable接口(支持克隆,即用现有实例相同的值创建类的新实例),然后用MemberwiseClone方法(创建当前System.Object的浅表对象)。但是需要注意的是MemberwiseClone拷贝方式,首先它是浅拷贝,方法是将所有的值类型字段拷贝一个副本,但是引用类型不会创建副本,仅仅是传递一个相同的地址给新对象,并且新对象和原对象指向的地址是一致的,这里有个问题就是string类型,但是实际上却是和值类型表现一致。

 

(3)深拷贝

深拷贝与浅拷贝不同的是对于引用字段的处理,深拷贝会将在新对象中创建一个新的对象和原始对象字段相同(内容相同)的字段,也就是说这个引用和原始对象引用是不同,我们改变新对象这中这个字段的时候是不会影响到原始对象中对应字段的内容。须实现ICloneable接口中的Clone方法,且需要将被克隆对象加上[Serializable]特性。

 

https://www.cnblogs.com/lsgsanxiao/p/9046080.html

posted @ 2020-09-07 15:37  不溯流光  阅读(583)  评论(0编辑  收藏  举报