堆和栈,类型声明实例化过程,string=null;string=”“的区别;
很多同学尤其是多数培训机构出来的同志,是直接学习的高级语言,诸如C#,JAVA
开发倒是够了,但有时也会问些比较底层的东西。
这里简单说一下,堆和栈,说到堆和栈又必须得说起引用,指针(被C#和JAVA屏蔽了,但是C#里可以写非安全型的指针)。
值类型,引用类型,指针类型。
区别:值类型直接入栈,引用类型通过指针访问,指针入栈,而该对象则在堆中,指针类型是特殊的类型,它也要分配内存,它的的值是所指向的对象的起始起址。
通常在高级语言开发,不用关心指针,C#由CLR去管理。
而且有非常重要的一点:C#和JAVA中,所说的对象的引用,其实质是指针,类似于C++和C语言的指针。
学过C语言或是C++的都知道,C语言里有也有引用。但这和C#面向对象的引用类型是两回事,而C#中的Ref,相当于C++和C语言的引用。
以C语言里的声名方式为例。
int a; 值类型。
int &b=a; 声明一个int型的引用。int型为2字节,则引用的地址+引用对像的类型所占的字节数,便是引用对象的结束地址。
声明对象的意义就是让程序知道,这个类型的对象所占的字节数。
而b相当于a的别名,操作b与操作a是同样的。
引用类型不占内存,主要用途是传值,指针类型则占内存,内存中的值,便是指向引用类型对象的首地址。
C#里的,ref 在IL里便是被解析为 & 引用类型。
详细请看这篇博文http://www.cnblogs.com/jacklondon/archive/2012/06/08/2542134.html
C#中有指针,但必须在Unsafe代码块中,JAVA则完全不支持指针。
首先我们要清楚,引用类型包括类、接口、委托和装箱值类型,string类型在IL被解析为String类型,也是引用类型。
假设定义了一个类型Customer
Customer cus; 声明一个Customer类型的引用(这实际是指针)。
cus = new Customer();
声明一个Customer类型的引用(指针的)的意义为两步。
1:定义一个指针,入栈。指针还未指向任何对象。
2:就是让程序知道,这个类型的对象所占的字节数,在内存中占多少空间。
而实际上这时还没有该对象存在,指针没有指向任何地址。
若只是声明,就使用该对象的属性或方法,理所当然出错。(根本不知道对象的入口在哪里)
Ps:为指针分配内存时,内存中是有值的,是有指向的,但值未知的。
再说操作对象属性和方法的过程。
要知道
对象的组成。对象内也不过是值和引用两种类型。
而对象的方法,实际和对象内的引用类型类似,指向的是一个地址(区别是方法的入口,对象的入口)
以值类型简单来说。假使A对象中有一个值类型属性c
A a=new A();
a.c
在调a.c时.
1先是在栈中找到A类的指针。
2指针的值即对象的入口。对象保存在堆中。例地址为0x00000004
3c属性,是相对于对象起始地址(入口)的偏移量。
想想后果,声明了一个对象,栈中分配了一个指针,指针的值未知(入口未知,偏移当然也是未知),天知道指哪去了,可能会指向另一个对象之中,这种操作,当然不安全。所以只声明不初始化。会有编译器会报错,有的编译时不报错,但在调属性或方法时出错。
cus = new Customer();
这一步才是真正的创建对象,在堆中分配内存,并把入口的地址保存在栈里指针cus的内存中,就是说,指针cus指向了堆里创建的这个对象。
如果只这样写
new Customer(); 在堆中分配内存,但没有指针指向它,那就等垃圾回收了。
而特别的
cus=null;
是C#或JAVA的实现,只声明,指针中的值是未知的。而指向null,只是一个标志。
有些语言,会把只声明,实现为指向空,这是语言底层实现的差别
不论是只声明(指针指哪没人知道),还是指向null(至少知道,指向为空),反正都没有指向真正的对象。
所以不论语言实现为声明(指向任意),还是指向空。
调用方法都会出错。
这时再说string类型就很清楚了。
string a; string a=null; 栈中有string类的指针,但要么指向值未知中,要么指向标志的Null,都没有真正的string对象。
string a="";则已经有了一个string对象存在(先这么说,实际上这里还有个String池的处理)。
我们知道,每实现一个对象,会在堆中新建。
String类对象的特别之处,是多了一个String池。
string a="nimei" string b="nimei" a b 指向的同一个String对象,目的是为了省内存。
但是String a=new String("nimei");String b=new String("nimei") a,b是不同的对象,每次NEW都在堆中新建一个新String.
个人觉得,String池,实质上也是堆,一个特别的堆。
再说点其他的
string a="" a在栈中,指向堆中一段“空”的String对象。
说是空但并不是真空。
C#和JAVA里可能没有提及。
C语言里string的实质是一个char类型的数组。
“abcde"的实际长度是6。
内存中的值是 abcde/0 /0表示字符串的结束。
所以C语言里string a=""; 实际是占内存的。内存的值就是/0;
但C#里,String是一个对象,和C语言不同
char数组有length属性。
String对象有Length()方法。
查IL,String类有个
public char this[int index]
{ [MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical, __DynamicallyInvokable]
get; }
反回char类型的索引,再下面我是看不懂了。
并没有明显的char[]存在,但是有一个GetEnumerbate方法
public CharEnumerator GetEnumerator() { return new CharEnumerator(this); } |
public sealed class CharEnumerator : ICloneable, IEnumerator<char>, IEnumerator, IDisposable
CharEnumerator 实现了IEnumerator<char> 接口
String对象,也一定和char数组有关,是包括char数组的对象,一些属性,方法的对象。