.Net 值类型和引用类型的一些认识

参考:http://www.cppblog.com/luyulaile/archive/2011/04/08/143703.html

前言

C#的值类型包括:结构体(数值类型,bool型,用户定义的结构体),枚举,可空类型。
C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。
数组的元素,不管是引用类型还是值类型,都存储在托管堆上。

值/引用类型的存储

栈(线程堆栈)是内存中完全用于存储局部变量或成员字段(值类型数据)的高效的区域,但其大小有限制。

托管堆所占内存比栈大得多,当访问速度较慢。托管堆只用于分配内存,一般由CLR(Common Language Runtime)来处理内存释放问题。

当创建值类型数据时,在栈上分配内存;

当创建引用型数据时,在托管堆上分配内存并返回对象的引用。注意这个对象的引用,像其他局部变量一样也是保存在栈中的。该引用指向的值则位于托管堆中。

如果创建了一个包含值类型的引用类型,比如数组,其元素的值也是存放在托管堆中而非栈中的。当从数组中检索数据时,获得本地使用的元素值的副本,而该副本这时候就是存放在栈中的了。所以,不能笼统的说“值类型保存在栈中,引用类型保存在托管堆中”。

值类型和引用类型的区别:引用类型存储在托管堆的唯一位置中,其存在于托管堆中某个地方,由使用该实体的变量引用;而值类型存储在使用它们的地方,有几处在使用,就有几个副本存在。

对于引用类型,如果在声明变量的时候没有使用new运算符,运行时就不会给它分配托管堆上的内存空间,而是在栈上给它分配一个包含null值的引用。对于值类型,运行时会给它分配栈上的空间,并调用默认的构造函数,来初始化对象的状态。

备注:1. 值类型数组虽然分配在堆上,但数组元素依然是值类型,并没有被装箱。
2, 引用对象的值类型成员也随对象一起分配在堆上,同样也还是值类型,没有被装箱

C# code

using System;

namespace ConsoleApplication3
{
    //值类型
    struct SomeVal
    {
        public int x;
    }
    //引用类型
    class SomeRef
    {
        public int x;
    }
    class Program
    {
        static void Main(string[] args)
        {
            SomeRef r1 = new SomeRef();//初始化,在堆中分配
            SomeVal v1 = new SomeVal();//初始化,在栈上分配
            Console.WriteLine(r1.x);//r1字段初始化为0
            Console.WriteLine(v1.x);//v1字段初始化为0
            r1.x = 5;//提领(解析)指针
            v1.x = 5;//在栈上直接修改
            SomeRef r2 = r1;//只复制引用(指针)
            SomeRef r3 = new SomeRef();//在堆中分配
            SomeVal v2 = v1;//现在栈上分配,然后拷贝成员
            r1.x = 8;//修改r1.x和r2.x
            //r2.x = 100;//修改r1.x和r2.x
            v1.x = 9;//修改v1.x而不修改v2.x
            r3.x = 10;
            Console.WriteLine(r1.x);//8
            Console.WriteLine(r2.x);//8
            Console.WriteLine(v1.x);//9
            Console.WriteLine(v2.x);//5
            Console.WriteLine(r3.x);//10
            Console.ReadKey();
        }
    }
}

预定义的引用类型

1、Object类

这是所有值类型和引用类型的最终基类,因为所有的类型派生自Object,所以可以把任何类型转换为Object类型,甚至值类型也可以转化(装箱

所有的值类型都派生自引用类型

2、String类

注意,string类型是属于引用类型。我们来看下面一段代码,在修改一个字符串的时候,实际上是创 建了一个新的字符串,而并非修改了原来在字符串。我们来看一个示例:

using System;
namespace Ref
{
    class StringRef
    {
        static void Main()
        {
            string str1 = "www";
            string str2 = str1;
            Console.WriteLine("str1=" + str1);
            Console.WriteLine("str2=" + str2);
            str1 = "www.baidu.com.cn";
            Console.WriteLine("str1=" + str1);
            Console.WriteLine("str2=" + str2);
            Console.ReadKey();
        }
    }
}

输出显示

str1="www";

str2="www";

str1="www.baidu.com.cn";

str2="www";

这和我们所预期的引用类型正好相反,为什么呢?

因为当我们用“www”来初始化str1的时候,就在堆上分配了一个

string对象,当初始化str2的时候,也指向了这个对象。当str1改变的时候,并不是修改了原有的对 象,而是新创建了一个对象,但str2还是指向原来的对象,所以,str2的值并未改

 值/引用类型的初始化问题

参考:http://blog.csdn.net/imfzp/article/details/4158826

 C# code

类中定义公共字段

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static int a ;
        static object b;
        static void Main(string[] args)
        {
            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.ReadKey();
        }
    }
}

可以编译通过

 方法中定义私有字段

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a;
            object b;
            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.ReadKey();
        }
    }
}

编译无法通过

原因在于在class的构造器会自动对所有field自动初始化,所以代码1编译可以通过,代码2方法里的私有字段确无法通过编译。

值类型初始化全部为0.....引用类型初始化为null

默认列表http://msdn.microsoft.com/zh-cn/library/83fhsxwc.aspx

数组的初始化

C#通过将初始值括在大括号 ({}) 内为在声明时初始化数组提供了简单而直接了当的方法。特别要注意的是,如果声明时未初始化数组,则数组成员自动初始化为该数组类型的默认初始值。

同理,double[](或int[]...等)初始化为0;

字符串的初始化

不存在无参数的构造函数

字符串的创建有8个方法的重载,7个需要输入char[],还有一个需要2个参数。。

常用方法是直接对其赋值初始化

string s = “initial”;

值/引用类型初始化的内存分配流程

比如创建一个对象:

Customer cus;

cus = new Customer();

申明一个Customer的引用cus,在堆栈上给这个引用分配存储空间。这仅仅只是一个引用,不是实际的Customer对象!

cus占4个字节的空间,包含了存储Customer的引用地址。

接着分配上的内存以存储Customer对象的实例,假定Customer对象的实例是32字节,为了在堆上找到一个存储Customer对象的存储位置。

 

.NET运行库在堆中搜索第一个从未使用的,32字节的连续块存储Customer对象的实例!

 

然后把分配给Customer对象实例的地址赋给cus变量!

参考http://bbs.bccn.net/thread-239785-1-1.html

例如声明string

1、string s;

  声明string s;只是在栈区存在引用,但是堆区没有分配内存,也就没有指向
没有指向的引用没有任何意义

2、string s = null;

  在堆中开辟内存空间(但应该很小吧),没有值但是有内存地址

值类型的声明

例如声明int

int i;

告诉编译器需要给我分配4字节的内存空间来存储i的值,在栈区开辟内存,但是没有任何值

posted on 2012-04-14 21:54  瘦肉微辣  阅读(288)  评论(0编辑  收藏  举报

导航