当前位置 博文首页 > 梦幻伯纳乌:从栈和堆中来看值传递和引用传递

    梦幻伯纳乌:从栈和堆中来看值传递和引用传递

    作者:[db:作者] 时间:2021-07-19 22:36

    1 栈和堆

    1.1?栈

    栈是一个内存数组,是一个LIFO(Last-In First-Out,后进先出)的数据结构。

    栈存储几种类型的数据:

    某些类型变量的值;

    程序当前的执行环境;

    传递给方法的参数。

    栈有如下几个特征:

    数据只能从栈的顶端插入和删除。

    把数据放到栈顶称为入栈(push)。

    从栈顶删除数据称为出栈(pop)。

    栈是在编译期,由编译器分配好内存空间。

    ?1.2 堆

    堆是一块内存区域,在堆中可以分配大块的内存用于存储某类型的数据对象。

    与栈不同,堆里的内存能够以任意顺序存入和删除。

    堆是在运行期,根据程序运行情况动态分配内存空间。


    2 值类型和引用类型

    C#数据类型分为两种:值类型和引用类型,这两种类型的对象在内存中的存储方式不同。

    值类型:只需要一段单独的内存,用于存储实际的数据。

    引用类型:需要两段内存。

    第一段存储实际的数据,它总是位于堆中。

    第二段是一个引用,指向数据在堆中的存放位置。

    一般情况下,对于值类型,数据存放在栈里。对于引用类型,引用存放在栈里,实际数据存放在堆里。如图所示:


    当然,上面是一般情况,值类型数据或引用类型的引用部分也会存放在堆中。

    比如,当一个值类型或一个引用类型是另一个引用类型对象的数据时,便会存放在堆中。

    C#中的值类型和引用类型如图所示:



    3 值参数和引用参数(值传递和引用传递)

    值参数:通过将实参的值复制到形参的方式把数据传递给方法。方法被调用时,系统在栈中为形参分配空间,然后将实参的值复制给形参。

    引用参数:在参数前面加 ref 或 out 修饰符 ,实参必须是变量。

    ref 和 out 的区别:ref参数传入方法之前必须初始化;out参数不需要,但out参数在方法返回时必需赋值。

    3.1 值类型数据作为值参数传递

    using System;
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                int val = 10;
                MyMethod(val);
                Console.WriteLine("val的值为:{0}",val);
            }
            static void MyMethod(int xxx)
            {
                xxx = xxx + 5;
                Console.WriteLine("xxx的值为:{0}",xxx);
            }
        }
    }
    

    结果如图所示:


    分析如图所示:


    3.2 值类型数据作为引用参数传递

    using System;
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                int val = 10;
                MyMethod(ref val);
                Console.WriteLine("val的值为:{0}",val);
            }
            static void MyMethod(ref int xxx)
            {
                xxx = xxx + 5;
                Console.WriteLine("xxx的值为:{0}",xxx);
            }
        }
    }
    
    结果如图所示:

    分析如图所示:


    3.3 引用类型数据作为值参数传递

    写了两个例子,注意在方法内部形参p的不同之处!

    示例1:

    using System;
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                Person p1 = new Person();
                p1.name = "lzj";
                MyMethod(p1);
                Console.WriteLine("p1.name的值为:{0}",p1.name);
            }
            static void MyMethod(Person p)
            {
                p.name = "CR7";
                Console.WriteLine("p.name的值为:{0}",p.name);
            }
        }
        class Person
        {
            public string name;
        }
    }
    
    结果如图所示:


    分析如图所示:


    示例2:

    using System;
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                Person p1 = new Person();
                p1.name = "lzj";
                MyMethod(p1);
                Console.WriteLine("p1.name的值为:{0}",p1.name);
            }
            static void MyMethod(Person p)
            {
                p = new Person() { name = "CR7" };
                Console.WriteLine("p.name的值为:{0}",p.name);
            }
        }
        class Person
        {
            public string name;
        }
    }
    
    结果如图所示:

    分析如图所示:

    3.4 引用类型数据作为引用参数传递

    using System;
    namespace Test
    {
        class Program
        {
            static void Main(string[] args)
            {
                Person p1 = new Person();
                p1.name = "lzj";
                MyMethod(ref p1);
                Console.WriteLine("p1.name的值为:{0}",p1.name);
            }
            static void MyMethod(ref Person p)
            {
                p = new Person() { name = "CR7" };
                Console.WriteLine("p.name的值为:{0}",p.name);
            }
        }
        class Person
        {
            public string name;
        }
    }
    
    结果如图所示:

    分析如图所示:

    总结

    作为值参数(即值传递)时:传递的是栈中的数据。

    作为引用参数(即引用传递时):传递的是栈本身的地址。





    cs