堆和栈,类型声明实例化过程,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数组的对象,一些属性,方法的对象。

 

 

 

 

posted @ 2013-06-11 19:21  cclient  阅读(473)  评论(0编辑  收藏  举报