C#如何理解值类型和引用类型/如何理解堆和栈

什么是值类型和引用类型?

值类型:

值类型主要包含整数类型,浮点类型和布尔类型等。

1.整数类型。用于储存整数数值,可正可负,下面是C#内置的八种整数类型:

 整数类型为什么通常使用int而不是short?

我的理解是因为提高代码整体的兼容性,如果使用short那么编辑代码的时候必须特别注意数值的大小,否则会导致运算溢出的错误。例如byte字节类型范围是0~255,当输入256时编译器会出现报错。

2.浮点类型。用于储存含小数的数值,下面是C#的三种浮点类型:

 为什么声明float类型的数值后必须加f或F?

因为C#中一经出现小数会默认其为double类型,所以我们声明float类型时,其实都要加一道强制将该小数从double类型指定为float类型的工序,例如float a = 1.1f;这就是为什么我们每次声明浮点型只有float后面要加f,double后面不用的原因,因为1.1d后面的d可以省略。

其中decimal类型在写脚本过程中不常用,可以把它理解成超高精度浮点型,只有在涉及财务和货币计算中会使用,int的位数已然足够解决生活中的运算域。

3.布尔类型。用于表示true和false值。通常在流程控制语句即循环语句或判断语句中使用。

扫描二维码关注公众号,回复: 14661480 查看本文章

要注意的是,整数和浮点数的默认初始化为0,布尔类型默认初始化是false;

引用类型

所有被称为“类”的都是引用类型,其中主要包括类、数组、接口和委托

值类型和引用类型的特点:

值类型的特点:

1.值类型变量都储存在栈中。

2.访问值类型变量时,都直接访问其实例。

3.复制值类型变量时,只复制值,不复制地址。

4.值类型变量不能为null,必须是一个具体的确定的值。

5.每个值类型变量都有自己的数据副本,因此对一个值类型变量操作不会影响到其他变量。

 引用类型的特点:

1.必须在堆中为引用类型变量分配内存。

2.引用类型被赋值前的值都是null。

3.使用new关键字来创建引用类型变量。

4.引用类型变量是由垃圾回收机制管理。

5.多个引用类型变量可以引用同一个对象,在这种情形下,对一个变量的操作会影响到另一个变量所引用的同一对象。

 如何理解堆和栈?

在值类型和引用类型的特点中,第一条就是它们的存储方式,我们了解到,值类型变量在栈中直接进行分配内存,而引用类型中的对象类型数据存储在堆中。

为什么要这样分配内存呢?

我们需要了解到,堆和栈都是内存空间的一部分。

。堆是一种由系统弹性配置的内存空间,没有特定大小及存活时间,因此可以弹性的运用于对象的访问。堆其实分为托管堆和非托管堆,我们这里的堆其实是托管堆的简称。托管堆和栈都由CLR(公共语言运行时)管理,我们这里只需知道CLR的核心功能是内存管理,程序集加载,安全性,异常处理,线程同步等。

。栈又称“线程栈”,顾名思义,它基于线程。它的空间比较小,在每开启一个新的线程时,从内存中开辟大约 1M 空间,作为该线程的自留地。线程栈是一个先进后出的栈数据结构,这样就保证了堆栈中先进后出的规则不与变量的生命周期起冲突并且一直都是连续的,因此栈内存的效率非常高

所以,对于值类型(非静态),初始化时,CLR会计算它需要的空间大小开辟一块空间,然后将其值储存在这块空间里。

而对于引用类型,它也会使用栈,但栈上仅仅储存一个地址(引用),这个地址指向托管堆中的对象,通过访问栈上的地址,就可以间接访问到堆上的引用类型对象,以及该对象的成员及变量值。建立对象引用的过程比建立值变量的过程复杂,所以不可避免性能会降低。

引用类型和值类型的区别是什么呢?

以下是储存值类型的图示过程:

而引用类型我通过代码+图示方便区别理解

namespace text1
{
    public class Student
    {
        public string Name;//定义引用类型
        public int Age;//定义值类型
    }

    public static class GetStudent
    {
        public static void Show()
        {
            Student a1 = new Student { Name = "LiHua", Age = 18 };
            Student a2 = new Student { Name = "ZhangSan", Age = 36 };
            int age = a1.Age;
            a1.Age = 20;
            Student b = a2;
            a2.Name = "LaoMaShu";
            Console.WriteLine(a1.Age);
            Console.WriteLine(age);
            Console.WriteLine(a2.Name);
            Console.WriteLine(b.Name);
        }
    }

    class Program
    {      
        static void Main(string[] args)
        {
            GetStudent.Show();
            Console.ReadLine();
        }
    }
}

这段代码的输出结果为:20 18 LaoMaShu LaoMaShu

如果混淆了值类型和引用类型储存方式的话,就会问为什么b.Name会输出LaoMaShu呢?

从结果可以看出,当改变了a1.Age的值后,age的值没跟着变;而在改变了a2.Name的值后,b.Name却跟着变了,这就是值类型和引用类型的区别

正如上面所说,在声明age值类型时,将a1.Age的值赋给它,这时,编译器在栈上分配了一块空间,然后把a1.Age的值填进去,二者毫无关联,相当于只把a1.Age的值复制给了age。

而引用类型则不同,在声明b时把a2赋给它。前面说过,引用类型包含的只是堆上数据区域地址的引用,其实就是把a2的引用也赋给b,因此它们指向了同一块内存区域。既然是指向同一块区域,不管修改谁,只要区域里的值变了,另一个的值也会跟着改变。类似于淘宝情亲号,一人下单,全家买单。

以下是储存引用类型的图示过程:

 PS:string字符串类型既没有在值类型提到,也没有在引用类型提到,那它是什么类型呢?

我们先来看一段代码:

    class Program
    {      
        static void Main(string[] args)
        {
            string a1 = "Da";
            string a2 = a1;
            Console.WriteLine(a1);
            Console.WriteLine(a2);
            a1 = "Shu";
            Console.WriteLine(a1);
            Console.WriteLine(a2);

            Console.ReadLine();
        }

这段代码的输出结果为:Da Da Shu Da

好的,上面说如果是引用类型的话把a1的值改了那a2的值也会跟着改,但是没有,所以这是值类型?          错

string类型是引用类型!

前面我们提到了基本数据类型包括:byte,short,char,int,long,float,double,bool八种。

而String类代表字符串,也就是说String 类并不是基本数据类型,而是一个类,属于引用类型。

string是一种特殊的引用类型,因为它具有不可变的特性,也就是说在C#中定义字符串时,它的string对象的值是不可改变的,从上面代码我们看到对a1进行了修改,实际上在对他进行修改的时候,相当于在内存中又生成了一个新的string对象,只不过这个对象的名称还是叫a1,但它实际上是一个全新的对象,对应全新的引用,而这个a2引用的还是原来的对象,所以a2的值并没有改变。

猜你喜欢

转载自blog.csdn.net/knight209208/article/details/128620826