• C# 堆与栈


    一、在讲堆栈之前,我们先看看值类型和引用类型:

    1,我们看看值类型与引用类型的存储方式:

    引用类型:引用类型存储在堆中。类型实例化的时候,会在堆中开辟一部分空间存储类的实例。类对象的引用还是存储在栈中。

    值类型:值类型总是分配在它声明的地方,做为局部变量时,存储在栈上;类对象的字段时,则跟随此类存储在堆中。

    什么是堆什么是栈我们后面解释。

                       图1-1

    2,我们再看看引用类型与值类型的区别:

    ①引用类型和值类型都继承自Systerm.Object类。不同之处,几乎所有的引用类型都是直接从Systerm.Object继承,而值类型则是继承Systerm.Object的子类Systerm.ValueType类。

    ②我们在给引用类型的变量赋值的时候,其实只是赋值了对象的引用;而给值类型变量赋值的时候是创建了一个副本(副本不明白?说通俗点,就是克隆了一个变量)。

    文字不够形象?我们上代码看看

                                           图1-2

     3,我们再看看引用类型和值类型的内存分配情况(我们对着代码与图看)

                                          图1-3

                                       图1-4

     看了图1-3和图1-4之后,你们可能问我了:你是怎么知道变量在栈中的地址的。嘿嘿,等下教你用c#玩指针

    从上面两张图我们可以看出:

    ①栈的结构是后进先出,也就是说:变量j的生命周期在变量s之前结束,变量s的生命周期在变量i之前结束,

    ②栈地址从高往底分配

    ③类型的引用也存储在栈中

    二、对于堆和栈的详细介绍,我们往下看。

    1,有人老是搞不明白堆和栈的叫法。我来解释下:

    堆:在c里面叫堆,在c#里面其实叫托管堆。为什么叫托管堆,我们往下看。

    栈:就是堆栈,因为和堆一起叫着别扭,就简称栈了。

    2,托管堆:

    托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理,当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。

    3,有人老是搞不清楚内存堆栈与数据结构堆栈,我们来看看什么是内存堆栈,什么是数据结构堆栈

    ①数据结构堆栈:是一种后进先出的数据结构,它是一个概念,图4-1中可以看出,栈是一种后进先出的数据结构。

    ②内存堆栈:存在内存中的两个存储区(堆区,栈区)。

          栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放

          堆区:存放着引用类型的对象,由CLR释放

     三、最后我们用c#玩一玩指针

    1,首先右键项目-->属性-->生成-->勾选允许不安全代码

    2,在写不安全代码的时候需要加上unsafe

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //标记类
    unsafe public class Student
    {
        //标记字段
        unsafe int* pAge;
        //标记方法
        unsafe void getType(int* a)
        {
            //标记代码段
            unsafe
            {
                int* pAbc;      //声明指针语法
            }
        }
    }

      

    3,指针的语法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    unsafe
    {
        int* pWidth, pHeight;
        double* pResult;
        byte*[] pByte;
        //&:表示“取地址”,并把一个值数据类型转换为指针,例如,int转换为*int。这个运算称为【寻址运算符】
        //*:表示“获取地址内容”,把一个指针转换为一个值数据类型(例如:*float转换为float)。这个运算符被
            //称为“间接寻址运算符”(有时称“取消引用运算符”)
        int a = 10;//声明一个值类型,给它赋值10
        int* pA, pB;//声明2个指针
        pA = &a;//取出值类型a的地址,赋值给指针pA
        pB = pA;//把指针pA的地址赋值给pB
        *pB = 20;//获取pB指向的地址内容,并赋值20
        Console.WriteLine(a);//输出20
    }

      

    4,将指针强制转化为整数类型(只能转化为uing、long、ulong类型)

    1
    2
    3
    4
    5
    6
    7
    int a = 10;
    int* pA, pB;
    pA = &a;
    uint address = (uint)pA;//将指针地址强制转换为整数类型
    pB = (int*)address;//将整数类型强制转换为指针
    *pB = 20;//指针指向a
    Console.WriteLine(a);//输出20

      

    5,指针类型的强制装换

    1
    2
    3
    4
    5
    int b = 10;
    int* pIa;
    double* pDa;
    pIa = &b;
    pDa = (double*)pIa;//将int*强制转换成double*

      

    6,void指针(不指向任何数据类型的指针)

    1
    2
    3
    4
    int c = 10;
    int* pAA;
    pAA = &c;
    void* pVa = (void*)pAA;//转换viod指针

      

    7,指针算数的运算(不允许对void指针进行运算)

    1
    2
    3
    4
    int d = 10;
    int* pId;//如果地址为100000
    pId = &d;
    pId++;//结果:100004(int类型的大小为4个字节)

      

    8,结构指针:指针成员访问运算符

    ①指针不能只想任何引用类型
    ②结构里面不能包含引用类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    MyStruct* pStruct;
    MyStruct ms = new MyStruct();
    pStruct = &ms;
    //--取地址内容赋值为10
    (*pStruct).X = 10;
    pStruct->X = 10;
    //--取地址赋值给pIs
    int* pIs1 = &(ms.X);
    int* pIs2 = &(pStruct->X);
    //结构
    struct MyStruct
    {
            public int X = 1;
            public int Y = 2;
    }

      

    9,类成员指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Person p = new Person();
    fixed (int* pIp1 = &(p.X), pIp2 = &(p.Y))
    {
    }//pIp1和pIp2的生命周期
    //类
    class Person
    {
         public int X;
         public int Y;
    }

      

    10,有趣的实验

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    unsafe
    {
    //有趣的问题:为什么b的值改变了
    //回答:因为i的地址指向了b
    int a = 10;
    int b = 20;
    int* i;
    i = &a;//将a的地址赋值给指针i
    i -= 1;//将i的地址-1(相当于向下移动4个字节(int类型的大小为4个字节))
    *i = 30;//取出i指针指向的内容赋值为30
    Console.WriteLine(b);//输出30
    }

      

  • 相关阅读:
    白话算法(6) 散列表(Hash Table)从理论到实用(中)
    白话算法(6) 散列表(Hash Table) 从理论到实用(下)
    基于无锁的C#并发队列实现
    邻接表建图的三种方式的时空比较(解析+图示)
    Windows内存管理(1)--分配内核内存 和 使用链表
    Windows内存管理(2)--Lookaside结构 和 运行时函数
    error C2443: operand size conflict
    CPUID 指令的使用
    Windows内核驱动开发入门学习资料
    重载内核全程分析笔记
  • 原文地址:https://www.cnblogs.com/summer2008/p/13723380.html
Copyright © 2020-2023  润新知