背景

  • 上次写了一篇文章关于try finnally的一些疑问(被我用windows live覆盖了,草),后来经过大神们解释,我明白了在我理解了try、finnally运行原理后,还欠缺的就是关于值类型引用类型在内存中的存储问题。

  • 我仔细拜读了一些大神们的文章,主要的参考点是一个系列文章《译文---C#堆VS栈》,里面详细解释了堆栈的要点,基础差的禽兽们就别看我这个总结了。

    什么是堆栈

  • 堆栈在内存中可以理解为两个不同的容器。

  • 栈:栈就是一个先入后出的存储空间,必须按照顺序依次执行。比如一筒羽毛球,只有一个出入口,如果想取出来球,只能先把最后塞入的那个球拿出来才能继续拿其他的球。

  • 堆:堆也是一个按序排列的存储空间,不同的是,堆里面的数据可以随便取。比如图书馆里的书都是按照一定顺序排列的,但是取书的时候,可以直接拿出来自己需要的书籍而不用管其他的书籍。

  • 数据在堆栈中的存储位置

  • 在堆栈上保存的数据分为四种:值类型、引用类型、指针、指令。

  • 值类型重点关注struct,因为其容易被认为成引用类型,而且其存储的数据有时候很大,传值时最好用ref;

  • 引用类型关注string,因为其虽然是引用类型,但是表现上是值类型,即通过形参传入的string数据的操作不会影响实参。

  • 指针通常不会由我们显示的使用,它们由公共语言运行时(CLR)来管理。指针(或引用)是不同于引用类型的,是因为当我们说某个事物是一个引用类型时就意味着我们是通过指针来访问它的。指针是一块内存空间,而它指向另一个内存空间。就像栈和堆一样,指针也同样要占用内存空间,但它的值是一个内存地址或者为空。(我复制的,我也不太懂)

  • 指令,在我参考的文档里好像并没有提到,我理解的是,当JIT编译器把方法的IL语句变为机器语言后,会将其存入到一个动态分配的内存空间,然后将地址返回给CLR的一个内部数据结构表(此表记录一个类里面的所有方法的地址),因为指令相当于值类型,具体存储位置可以参考值类型。

  • 类型存在位置的定律

    1. 引用类型总是在堆上创建。

    2. 值类型和指针类型总是在它声明的地方创建。

    引用类型总是在堆上创建,然后将地址返回,地址的保存位置类似与值类型。

    值类型的存储位置和声明它的类型有关。当Main()执行的时候,会分配一个栈区,如果在Main()里面声明了值类型,那么它就会保存在这个栈上;如果Main()先声明了一个struct,而struct里面声明了值类型,那么这个值类型也一样保存在栈上,因为声明这个值类型的类型即struct也在栈上;而如果声明值类型的类型是一个引用类型,比如一个类,那么这个值类型就会保存在堆上。

    赋值时的区别

    值类型赋值就是一种深拷贝,即将值的内容传递过去,且不会受相互操作的影响。

    引用类型赋值是一种浅拷贝,只是将引用类型的地址传递了过去,比如A = B,那么A、B的操作都会相互影响。当然,string是一种特例,赋值或者传参后不会相互影响。如果你想引用类型也能执行深拷贝,就要自己写Clone()方法了。

    空间分配及回收

  • 堆和栈这里的区别具体不解释,可以查看相关文档,这里引用一位前辈的比喻来看出: 
       使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。 

    使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。比喻很形象,说的很通俗易懂,不知道你是否有点收获。

    举例

  • 以下是我想的两个例子,如果你都能答对且明白原理,则说明你至少理解了堆栈。

    1、引用类型赋值,修改某一个引用类型,会有什么结果

           static void Main(string[] args)
    {
    var my = new MyInt();
    var you = my;
    you.index = 1;
    you = null;
    Console.WriteLine(my.index.ToString());
    Console.ReadLine();
    }
    public class MyInt
    {
    public int index;
    }
  • 结果是:1;而且没有返回异常。(重点是为什么没发生异常)?

    2、引用类型传递和指针传递(ref代表传递的是指针)的区别

          static void Main(string[] args)
    {
    Program main = new Program();
    var my = new MyInt();
    main.CommonMethod(my);
    Console.WriteLine(my.index.ToString());
    main.RefMethod(ref my);
    Console.WriteLine(my.index.ToString()); Console.ReadLine();
    } public void CommonMethod(MyInt myInt)
    {
    myInt.index = 1;
    myInt = null;
    } public void RefMethod(ref MyInt myInt)
    {
    myInt.index = 2;
    myInt = null;
    }
    public class MyInt
    {
    public int index;
    }

  • 结果是1,然后异常;(为什么引用类型传递不异常,而指针传递异常)?


    疑问

  • 数据结构的栈和内存中的栈有什么区别吗?网上说数据结构的栈是一个理论,一个是实现方式,那么数据结构中的队列,在内存中有什么实现呢?还是说网上的说法是错误的?

  • 希望大神们帮我解释一下。

    C#堆栈原理(我有两个例子测试你到底会不会)的更多相关文章

    1. 痞子衡嵌入式:嵌入式里堆栈原理及其纯C实现

      大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家讲的是嵌入式里堆栈原理及其纯C实现. 今天给大家分享的这篇还是2016年之前痞子衡写的技术文档,花了点时间重新编排了一下格式.栈这种结构在嵌入式 ...

    2. (3两个例子)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练

      从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练 1综述http://www.cnblogs.com/jsxyhelu/p/7907241.html2环境架设http://www.cn ...

    3. 解决Linux终端乱码的两则例子

      现象描述 我们先来说一下出现乱码的原因. 例子 先举个实际的例子,我们一般通过ssh远程到服务器上进行操作.当在终端上执行一些有输出的任务时,有可能会遇到乱码,特别是输出中有中文时. 比如,我登陆上o ...

    4. HTTP基础(分析两个例子)

      两个例子(一个get,一个post)(一个是访问页面,一个是提交修改后的博文): preferences.aspx:(header)(文件) 1.     Remote Address:42.121. ...

    5. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

      机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

    6. 论cudnn与cuda之间的关系,和实际例子测试。

      1.其中cudnn是一个常见的神经网络层加速库文件,其能够很大程度的把加载到显卡上的网络层数据进行优化计算.cuda就像一个傻大粗的加速库,其主要是依靠的是显卡 计算速度跟一些算法的优化,而且其也是进 ...

    7. tkinter 的两个例子

      第一个例子:after 用于定时操作 import tkinter as tk import time class MyApp(tk.Frame): def __init__(self, msecs= ...

    8. 探讨instanceof实现原理,并用两种方法模拟实现 instanceof

      在开始之前先了解下js数据类型 js基本数据类型: null undefined number boolean string js引用数据类型: function object array 一说ins ...

    9. 161223、mysql锁的两个例子

      版本:mysql5.5.52 存储引擎:InnoDB 隔离级别:READ-COMMITTED 示例一: 事务1:左图      事务2:右图 1. 事务2中属于快照读,基于多版本的并发控制协议--MV ...

    随机推荐

    1. 14.6.4 Configuring the Memory Allocator for InnoDB 配置InnoDB 内存分配器

      14.6.4 Configuring the Memory Allocator for InnoDB 配置InnoDB 内存分配器 当InnoDB 被开发时,内存分配提供了操作系统和 run-time ...

    2. 算法-KMP模式匹配算法

      1朴素算法:逐个比较 2 主要是解决多余比较的麻烦,通过处理比较字符串是否含有重复的字符的问题.

    3. java学习面向对象之异常之一

      一.异常的概述: 什么是异常?在我们编写java程序的时候,会出现一些问题,比如内存溢出啊或者数组索引超出最大索引啊,这些编程当中出现的这些个问题就是异常.但是异常也分为可以处理的和不可以处理的.比如 ...

    4. bzoj1853 bzoj2393

      两题是类似的,这里说一下bzoj1853 首先我们求出所有的幸运号码,注意如果存在x是y的倍数则x不算在内,避免之后重复计算 下面我们就要统计幸运号码的倍数了,这显然是要用到容斥原理的 但是幸运号码很 ...

    5. 深入浅出Node.js (附录D) - 搭建局域NPM仓库

      D.1 NPM仓库的安装 D.1.1 安装Erlang和CouchDB D.1.2 搭建NPM仓库 D.2 高阶应用 D.2.1 镜像仓库 D.2.2 私有模块应用 D.2.3 纯私有仓库 D.3 总 ...

    6. 神器 Sublime Text 3 的一些常用快捷键

      选择类   Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本. Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑.举个例子:快速选中并更改所有相同的变量名. ...

    7. vimium快捷键列表

      最近越来越懒了,不想拿手去碰鼠标,就想这样放在键盘上,在MacOSX下基本的操作也都能实现了,Xcode也没什么问题,现在就是有个地方十分不方便,就是浏览网页的问题,不管怎么样都是需要鼠标来浏览网页, ...

    8. App被拒绝的原因收录

      转自:dApps开发者 » APP被苹果App Store拒绝的79个原因(未完待续) 1.程序有重大bug,程序不能启动,或者中途退出.2.绕过苹果的付费渠道,我们之前游戏里的用兑换码兑换金币.3. ...

    9. shared pool详解

      共享池shared pool的概念用户提交的命令:解析.执行用户命令的解析解析的过程是一个相当复杂的过程,它要考虑各种可能的异常情况比如SQL语句涉及到的对象不存在.提交的用户没有权限等等而且还需要考 ...

    10. 查询grep结果的前后n行

      linux系统中,利用grep打印匹配的上下几行   如果在只是想匹配模式的上下几行,grep可以实现.   $grep -5 'parttern' inputfile //打印匹配行的前后5行   ...