.NET的堆和栈02,值类型和引用类型参数传递以及内存分配
在" .NET的堆和栈01,基本概念、值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配。我们知道:当执行一个方法的时候,值类型实例会在"栈"上分配内存,而引用类型实例会在"堆"上分配内存,当方法执行完毕,"栈"上的实例由操作系统自动释放,"堆"上的实例由.NET Framework的GC进行回收。而本篇的重点要放在:值类型和引用类型参数的传递,以及内存分配。
主要包括:
■  传递值类型参数
■  传递容易造成"栈溢出"的值类型参数,在值类型参数前加关键字ref
■  传递引用类型参数
■  传递引用类型参数,在引用类型参数之前加关键字ref
传递值类型参数
class Class1
{
public void Go()
{
int x = 5;
AddFive(x); Console.WriteLine(x.ToString()); } public int AddFive(int pValue)
{
pValue += 5;
return pValue;
}
}
大致过程如下:
1、值类型变量x被放到"栈"上。
2、开始执行AddFive()方法,值类型变量pValue被放到"栈"上,并把x的值赋值给pValue,pValue的值变成了5。
3、继续执行AddFive()方法,pValue的值变成了10。
4、执行完AddFive()方法,释放pValue的内存,"栈"指针回到x,线程重新回到Go()方法中。
输出结果:5
以上,在传递值类型参数x的时候,实际上是把x一个字节一个字节地拷贝给pValue。
传递容易造成"栈溢出"的值类型参数,在值类型参数前加关键字ref
public struct MyStruct
{
long a, b, c, d, e, f, g, h, i, j, k, l, m;
} public void Go()
{
MyStruct x = new MyStruct();
DoSomething(x); } public void DoSomething(MyStruct pValue)
{
// DO SOMETHING HERE....
}
假设以上的值类型struct足够大,而x和pValue都会被分配到"栈"上,这时可能造成"栈溢出"。

如何避免呢?
--解决办法是让DoSomething传递一个ref类型参数。这样写:
public struct MyStruct
{
long a, b, c, d, e, f, g, h, i, j, k, l, m;
} public void Go()
{
MyStruct x = new MyStruct();
x.a = 5;
DoSomething(ref x); Console.WriteLine(x.a.ToString()); } public void DoSomething(ref MyStruct pValue)
{
pValue.a = 12345;
}
使用ref后,执行DoSomething(ref x),是把x的地址赋值给了pValue,即pValue和x指向了同一个引用地址。当改变pValue的值,变化也会反映到x中。

输出结果:12345
以上,为了避免"大型"值类型参数传递时造成的"栈溢出",可以在值类型前面加ref关键字,于是,在传递值类型参数x的时候,实际上是把x本身的栈地址拷贝给pValue,x和pValue指向同一个栈地址。
传递引用类型参数
传递引用类型参数的道理和在传递的值类型参数前面加ref关键字是一样的。
public class MyInt
{
public int MyValue;
} public void Go()
{
MyInt x = new MyInt();
x.MyValue = 2; DoSomething(x); Console.WriteLine(x.MyValue.ToString()); } public void DoSomething(MyInt pValue)
{
pValue.MyValue = 12345;
}
输出结果:12345
以上大致过程是这样:
1、在托管堆上创建一个MyInt类型的实例
2、在栈上创建一个MyInt类型的变量x指向堆上的实例
3、把托管堆上的公共字段MyValue赋值为2
4、通过DoSomething(x)方法,把x的引用地址赋值给pValue,即pValue和x指向同一个引用地址
5、改变pValue的值,也会反映到x上
以上,在传递引用类型参数x的时候,实际上是把x指向托管堆实例的引用地址拷贝给pValue,x和pValue指向同一个托管堆实例地址。
传递引用类型参数,在引用类型参数之前加关键字ref
public class Thing
{
} public class Animal:Thing
{
public int Weight;
} public class Vegetable:Thing
{
public int Length;
} public void Go()
{
Thing x = new Animal(); Switcharoo(ref x); Console.WriteLine(
"x is Animal : "
+ (x is Animal).ToString()); Console.WriteLine(
"x is Vegetable : "
+ (x is Vegetable).ToString()); } public void Switcharoo(ref Thing pValue)
{
pValue = new Vegetable();
}
输出结果:
x is Animal    :   False
x is Vegetable :   True
以上大致过程是这样:
1、在托管堆上创建Animal对象实例。
2、在栈上创建类型为Thing的x变量指向Animal实例的引用地址。
3、通过Switcharoo(ref x)方法把x本身的地址赋值给pValue,至此,pValue和x指向了相同的栈内存地址,任何一方的变化都会反映到另外一方。

4、在Switcharoo(ref Thing pValue)内部,在托管堆上创建Vegetable对象实例。
5、pValue指向Vegetable实例,也就相当于x指向Vegetable实例。

以上,当在引用类型参数之前加上关键字ref,再传递,是把x本身的栈地址拷贝给pValue,x和pValue指向同一个栈地址。
参考资料:
C# Heap(ing) Vs Stack(ing) in .NET: Part II
《你必须知道的.NET(第2版)》,作者王涛。
".NET的堆和栈"系列包括:
.NET的堆和栈01,基本概念、值类型内存分配
.NET的堆和栈02,值类型和引用类型参数传递以及内存分配
.NET的堆和栈03,引用类型对象拷贝以及内存分配
.NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配
.NET的堆和栈02,值类型和引用类型参数传递以及内存分配的更多相关文章
- 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
		
为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
 - .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)
		
作者: Edison Chou 来源: 博客园 发布时间: 2014-09-03 15:59 阅读: 318 次 推荐: 2 原文链接 [收藏] 原文作者:Shivprasad k ...
 - .NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱
		
为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
 - [转]  .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
		
为何要转载 一来是最近面试了几家公司,发现问的还都是这些的基础知识,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果. 为什么有人说,不动笔不读书.我现在也是深有体会了,看过的东西不一定会记得 ...
 - (11)C#值类型和引用类型,堆和栈,ref和out,装箱和拆箱
		
一.值类型和引用类型定义 以内存中的存在方式可以把变量分成两大类型,值类型和引用类型. 值类型:系统只占用一块内存,数据直接存储在内存里. 引用类型:系统占用两块内存,一块存放地址,另一块存放实际数据 ...
 - 第2篇 C#数据类型-值类型与引用类型
		
一 C#内存分配 在应用程序与操作系统之间有一个"中间人"--公共语言运行时(Common Language Runtime,CLR).它为应用程序提供内`存管理,线程管理和远程处 ...
 - foreach 引发的值类型与引用类型思考
		
用都知道的一句话概括:“引用类型在堆上,栈上只保存引用:值类型即可存放于栈上也可存放于堆上,值类型变量直接存储值本身”. class Program { static void Main(string ...
 - C# 值类型与引用类型的详解
		
值类型与引用类型分这几种情况: 1.内存分为堆和栈,值类型的数据存储在栈中,引用类型的数据存储在堆中. 2.int numb=10,代码中的10是值类型的数据,numb只是一个指向10的变量而已.其中 ...
 - C#学习笔记之值类型与引用类型
		
[TOC] C#学习笔记之值类型与引用类型 1.值类型与引用类型 1.1 深层区别 值类型与引用类型有不同的内存分布,这导致了不同的内存管理机制: 值类型由OS负责内存管理 引用类型由垃圾回收器(GC ...
 
随机推荐
- sqlserver导出为EXcel--CSV格式
			
DataTable dt = Connect.BindTable(" SELECT 名称,地址,当前日期 FROM table GROUP BY 名称,地址,当前日期 order ...
 - 利用Octopress在github pages上搭建个人博客
			
利用Octopress在github pages上搭建个人博客 SEP 29TH, 2013 在GitHub Pages上用Octopress搭建博客,需要安装ruby环境.git环境等.本人在Fed ...
 - Vue.js学习笔记(一) - 起步
			
本篇将简单介绍一下Vue.js,并在Node.js环境下搭建一个简单的Demo. 一.简介 我个人理解,Vue.js是一套前端视图层的框架,它只关心视图展示和数据绑定,它的一些语法与Angular 1 ...
 - 【LOJ】#2056. 「TJOI / HEOI2016」序列
			
题解 这个我们处理出来每一位能变化到的最大值和最小值,包括自身 然后我们发现 \(f[i] = max(f[i],f[j] + 1) (mx[j] <= a[i] && a[j] ...
 - 【LOJ】#2340. 「WC2018」州区划分
			
题解 学习一个全世界人都会只有我不会的东西 子集变换! 难道我要把这题当板子讲?等等这题好像是板...WC出板题好刺激啊= = 假装我们都做过HAOI2015的FMT题,我们都知道一些FMT怎么解决或 ...
 - jdk1.8下字符串常量的判断,String.intern()分析
			
字符串常量池在jdk升级过程中发生了一些变化 在JDK1.6中,它在方法区中,属于“永久代”. 在JDK1.7中,它被移除方法区,放在java堆中. 在JDK1.8中,取消了“永久代”,将常量池放在元 ...
 - Bomb  HDU 3555  dp状态转移
			
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题意: 给出一个正整数N,求出1~N中含有数字“49”的数的个数 思路: 采用数位dp的状态转移方程 ...
 - 希尔排序之C++实现(高级版)
			
希尔排序之C++实现(高级版) 一.源代码:ShellSortHigh.cpp /*希尔排序基本思想: 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组. 所有距离为d1的倍数的记录放在同 ...
 - 在线HTTP速度测试(响应时间测试)及浏览器兼容测试
			
一.前言 网站的响应时间,是判断一个网站是否是好网站的重要的因素之一.百度首页的响应时间在全国各个省份小于10ms.这个响应时间远远好于竞争对手.根据美丽说的技术负责人分析,美丽说访问速度提升10%, ...
 - PHP代码为什么不能直接保存HTML文件——>PHP生成静态页面教程
			
1.服务器会根据文件的后缀名去进行解析,如果是HTML文件则服务器不会进行语法解析,而是直接输出到浏览器. 2.如果一个页面中全部都是HTML代码而没有需要解析的PHP语法,则没有必要保存为PHP文件 ...