在C#中,变量的类型就属引用类型,值类型,以及他们之间相互的转换比较难理解,里面更是涉及到了类型在内存中的存储结构,本文通过内存,栈,堆,值类型,引用类型的关系,以及相互转换时产生的装拆箱操作,来给大家梳理一下其中的过程,拨开各种层层的技术迷雾,探究其真正的本质。如果大家对过程产生疑问或者描述过程有错误的地方,欢迎在评论区中多多指正,大家一起学习,一起进步! 内存内存的物理结构
在讲数据结构之前,和大家先一起回顾下内存的物理结构是啥,内存的物理结构比较简单,大部分人都见过内存条: 抽象出来之后的内存条模型: 内存实际上是一种名为内存IC的电子元件,内存IC中有 电源、地址信号、数据信号、 控制信号等用于输入输出的大量引脚(IC的引脚),通过为其指定地址,来进行数据的读写。VCC和GND是电源,A0~A9是地址信号的引脚,D0~D7是数据信号的引脚,RD和WR是控制信号的引脚。将电源连接到VCC和GND后,就可以给其他引脚传递比如0或者1这样的信号。大多数情况下,+ 5V的 直流 电压表示1,0V表示0。 上面的内存IC能存储多少数据呢,数据信号引脚有D0~D7共八个,表示一次可以输入输出8位(=1字节)的数据。此外,地址信号引脚有A0~A9共十个,表示可以指定0000000000~1111111111共1024个地址。而地址用来表示数据的存储场所,因此我们可以得出这个内存IC中可以存储1024个1字节的数据。因为1024=1K,所以该内存IC的容量就是1KB。 现在大家使用的计算机至少有512M的内存。这就相当于512000个(512MB÷1KB=512000K)1KB的内存IC。当然,一台计算机中不太可能放入如此多的内存IC。通常情况下,计算机使用的内存IC中会有更多的地址信号引脚,这样就能在一个内存IC中存储数十兆字节的数据。因此,只用数个内存IC,就可以达到512MB的容量。如上实图1GB的内存条,引脚比较多。 内存的读写
内存的写入的实现,我们继续来看刚才所说的1KB的内存IC。首先,我们假设要往该内存IC中写入1字节的数据。为了实现该目的,可以给VCC接入+5V,给GND接入0V的电源,并使用A0~A9的地址信号来指定数据的存储场所,然后再把数据的值输入给D0~D7的数据信号,并把WR(write=写入的简写)信号设定成1。执行完这些操作,就可以在内存IC内部写入数据(如下图中的a)了。 内存的读取的实现,读出数据时,只需通过A0~A9的地址信号指定数据的存储场所,然后再将RD(read=读出的简写)信号设成1即可。执行完这些操作,指定地址中存储的数据就会被输出到D0~D7的数据信号引脚(下图中的b)中。 另外,像WR和RD这样可以让IC运行的信号称为控制信号。其中,当WR和RD同时为0时,写入和读出的操作都无法进行。 内存IC内部有大量可以存储8位数据的地方,通过地址指定这些场所,之后即可进行数据的读写。 内存的逻辑模型
内存的逻辑模型可以简单理解为每层都存储着数据的楼房,在这个楼房中,1层可以存储1个字节的数据,楼层号表示的就是地址。同时并不需要过多地关注内存IC的电源和控制信号等。当内存为1KB时,表示有1024层的楼房( 编程语言的数据类型空间没有在这里体现): 在程序中,可以指定数据类型的占用空间的大小(占用的楼层数),程序定义三个变量,内存实际存放的空间如下:
byte a=123; short b=123; int c=123;
其中,字节是二进制数据的单位,常用的字节是8位的字节,即包含8位的二进制数,因此,4字节就是32位,一个字节有符号代表2^7=128 ,无符号代表2^8=256。 内存地址的解析
地址,是用来标志存储资源位置的,在计算机中用一串二进制数据表示,其中包含: - 物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址,编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地址是明确的、最终用在总线上的编号,不必转换,不必分页,也没有特权级检查。
- **逻辑地址:**CPU所生成的地址。逻辑地址是内部和编程使用的、并不唯一,逻辑地址的生成依赖与编译器(源代码编译为机器码)。
下图是CPU和计算机的基本架构,我们以此图来说明物理/逻辑地址在CPU和计算机中如何被解析处理的: - CPU中的算数逻辑单元看到的都是逻辑地址
- 当CPU需要把数据写入内存或从内存中读取时,MMU会把逻辑地址转换成对应的物理地址
- 控制逻辑把数据、操作请求和物理地址发送到总线,分为读请求和写请求(写请求,则把数据写入内存,读请求,则把数据从内存中读取发送给CPU)
- MMU负责逻辑地址和物理地址之间的转换
- 操作系统负责建立逻辑地址和物理地址之间的映射关系Windows使用了虚拟寻址系统技术,把程序的可用内存地址映射到 硬件内存中的实际地址中,这些任务完全由Windows后台管理,实际结果是32位处理器上的每个进程都可以使用4GB的内存,有2G的用户模式内存(无论计算机上有多少硬盘空间(在64位处理器上这个数值会更大))。这个4GB内存,2G用户模式内存也叫虚拟地址空间,或叫虚拟内存
当然,这里也涉及到了许多内存管理相关的知识,比如连续的内存分配,非连续的内存分配的方式(虚拟内存)进行内存的相关管理和优化。有兴趣的同学可以再深入探索。 程序在内存的分配
当一个exe程序(内容为再分配信息,变量组和函数组)被点击时,此时程序会被加载到虚拟内存中,并且从虚拟内存地址转换成实际的内存地址。虚拟内存会为程序额外生成2个组,那就是栈和堆。 栈是内存数组,是一个后进先出的数据结构(先进先出的称为队列),栈也成为堆栈,线程堆栈,每个正在运行的程序都对应着一个进程(或几个,但是一个进程只能对应一个应用程序)在一个进程内部,可以有一个或多个线程(thread),每个线程都拥有一块“自留地”,称为“线程堆栈”,大小为1M,栈存储下列的几种类型数据: - 某些类型变量的值(值类型)
- 程序当前的执行环境
- 传递给方法的参数
这部分的内存区域分配和释放不需要程序员管理 堆是内存的一块区域,在堆里可以分配大块的内存用于存储某类型的数据对象,C#中称为托管堆,由CLR进行管理,与栈不同,堆里面的内存能够以任务的顺序存入和移除。同时因为这个特点,也会造成堆存储的空间不连续,需要GC进行相应的处理。 exe文件中并不存在栈和堆的组。栈和堆需要的内存空间是在exe文件加载到内存后开始运行时得到分配的。因而,内存中的程序,就是由用于变量(全局变量,静态变量,常量)的内存空间,用于函数的内存空间,用于栈的内存空间,用于堆的内存空间这4部分构成的。当然在内存中,加载windowds等操作系统的内存空间又是另外一回事了。 如下图所示: 栈及堆的相似之处在于,他们的内存空间都是在程序运行时得到申请分配的。不过,在内存的使用方法上,二者存在些许不同。栈中对数据进行存储和舍弃(清理处理)的代码,是由编译器自动生成的,因此不需要程序员的参与。使用栈的数据的内存空间,每当函数被调用时都会得到申请分配,并在函数处理完毕后自动释放。与此相对,堆的内存空间,则要根据程序员编写的程序,来明确进行申请分配或释放。根据编程语言的不同,对堆用的内存空间进行申请分配和释放的程序的编写方法也是多种多样的。 比如C语言需要程序员调用对应的方法函数来手动申请分配和释放,C++需要用运算符来申请和释放,当然C和C++不好操作的地方在于,如果没有在程序中明确释放堆的内存空间,那么即使在处理完毕后,该内存空间仍会一直残留。这个现象称为内存泄露。而C#和Java,都是使用GC自动垃圾回收机制来处理相关的问题,使得程序员不需要关心堆在内存中的管理问题。关于GC如何工作的,请移步:https://docs.microsoft.com/zh-cn/dotnet/standard/garbage-collection/ 下面我们来具体看下数据在栈和堆中是怎么分配和存储的 栈和堆为啥内存中既然有了栈,为啥还用堆这种内存空间。因为栈的工作的方式是先分配内存的变量后面才释放(先进后出),是从上往下填充(高内存地址到低内存地址)。但是很多变量不是单独存在的,可能和其他的变量嵌套,这样就和变量的生命周期起了冲突,为了解决这个问题,堆的 设计就是从下往上分配,保证了栈中先进后出的规则不与变量的生命周期起冲突。为啥堆能解决冲突,还要设计栈这个结构呢,因为全部变量保存在堆中,会使得应用程序性能下降。 在介绍数据在栈和堆的存放原理时,需要介绍下C#中的数据类型 C#数据层次结构
数据类型如下如图所示: 数据类型分:值类型和引用类型。值类型存放在栈(堆栈)中,引用类型先在栈中存放对应的引用地址,然后在堆(托管堆)中分配空间存放数据。
Student st;
st=new Student();
声明st的对象引用的时候,会在栈中存放对应的引用地址(占用4个字节的空间,地址此时是空的信息,因为还没创建对应的实例对象),这里仅仅是一个引用地址的信息存放,不是对应Student对象,接着第二行代码,堆中的内存会给Student对象分配内存空间,假定Student对象的实例是32个字节,CLR需要搜索一个未使用且连续的内存空间来存储对象的实例(大小为32*8位字节,同时需要提领指针,把分配给Student对象的实例地址赋值给st变量),如果没有,这个时候,会涉及到GC强制的一次垃圾回收,如果回收后空间还是不够,会抛出内存不足异常。 上面的例子告诉我们,建立对象引用的过程比建立值变量的过程复杂,且不能避免性能的降低,为了提升简单和常用的类型的性能,CLR提供了名为“值类型”的轻量级类型。值类型实例变量不包含指向实例的指针。相反,变量中包含了实例本身的字段,由于变量已包含了实例的字段,所以操作实例中的字段不需要提领指针(Int32 a=new Int(); 所以值类型也是有实例对象)。值类型的实例不受垃圾回收器的控制。因此,值类型的使用缓解了托管堆的 压力,并减少了应用程序生存期内的垃圾回收的次数,提升了性能。 下面通过例子来演示引用类型和值类型的区别:
class StudentRef{
public int age;
}
struct StudentVal{
public int age;
}
static void ValueTypeDemo(){
StudentRef r1=new StudentRef();
StudentVal v1=new StudentVal();
r1.age=18;
v1.age=18;
Console.WriteLine(r1.age);
Console.WriteLine(v1.age);
StudentRef r2=r1;
StudentVal v2=v1;
r1.age =20;
v1.age =21;
}
如图:(对象指针的作用是用来关联对象,同步索引的作用是用来完成同步(比如线程同步)) 下面来看更加复杂的对象的存储:
class Student{
public void Study(){
Console.WriteLine("学习!");
}
public virtual int Credit(int x,int y){
Console.WriteLine($"总学分:{x+y},必修:{x},选修:{y}");
return x+y;
}
public static void Play(string s){
Console.WriteLine("玩耍:"+s);
}
}
class Freshman:Student{
public override int Credit(int x,int y){
Console.WriteLine($"大一学生总学分:{x+y}");
return x+y;
}
}
public void XiaoMing(){
int score;
Student xm=new Freshman();
score=xm.Credit(30,5);
xm.Study();
xm.Play("游戏");
}
如图: 说明: - 进程 启动,CLR加载到其中,托管堆初始化,创建线程(1M的栈空间),线程已经执行一些代码,后调用XiaoMing()方法时,JIT编译器将方法中的IL代码转换为本机CPU指令,加载方法中定义的类型涉及到的程序集,CLR提取相关数据到内存数据区,并初始化/创建一些数据结构来表示类型本身
-
收藏
分享
邀请
上一篇: 上位机编程系列开发语言C#,与PLC通信下一篇: WINCC经典组态页面汇总学习
相关阅读
- • 如何通过WINCC组态软件生成EXCEL报表,附脚本代码
- • python和c,应该先学哪个?
- • C# Linq 查找 List<> 集合元素
- • WinCC的VBS脚本z中的过程函数如何应用
- • html 常用的 DOCTYPE 声明
- • 你该知道的C++四种显示类型转换
- • TCP通信程序设计方法
- • js 判断是否为手机端、移动端访问
- • VC++之上位机编程
- • C语言编程学习:数据类型的转换
- • 博途中WinCC VBS 脚本的基础用法
- • C# 数据类型在内存中的存储原理
- • WinCC全局脚本编辑器
- • TCP 实现客户端之间通信(全套代码)
- • Wincc中常用的C脚本实例
- • WinCC脚本系统及动作、函数和触发器的概念简介
- • C#实例:datagridview单元格合并
- • 上位机编程系列开发语言C#,与PLC通信
- • C# 操作Linq, 查找, 获取, 合并, 排序
- • C语言局部变量 静态局部变量 全局变量与静态全局变量
- • C语言指针讲解,赶快收藏
- • 串口通信基础知识
相关分类
- 变频器学习
- PLC学习
- 液压控制
- 机器人
- 上位机编程
- 电工电气
- 传感器仪表
- 自动化控制
- 弱电工程
热门文章
- C# 操作Linq, 查找, 获取, 合并, 排序
- C# Linq 查找 List<> 集合元素
- WINCC经典组态页面汇总学习
- WinCC与S7-1500 R/H冗余系统的通信解析
- 上位机编程系列开发语言C#,与PLC通信
- python和c,应该先学哪个?
- html 常用的 DOCTYPE 声明
- 串口通信基础知识
- js 判断是否为手机端、移动端访问
- TCP/IP、HTTP协议的区别
- C#实例:datagridview单元格合并
- C语言局部变量 静态局部变量 全局变量与静
- C# 数据类型在内存中的存储原理
- TCP 实现客户端之间通信(全套代码)
- 如何使用C脚本实现WinCC C单按钮取反?
- 如何通过WINCC组态软件生成EXCEL报表,附脚
- C语言指针讲解,赶快收藏
- C语言编程学习:数据类型的转换
- 你该知道的C++四种显示类型转换
- Wincc中常用的C脚本实例
- VC++之上位机编程
- TCP通信程序设计方法
- Java线程和操作系统的线程到底有啥区别?
- WinCC全局脚本编辑器
- WinCC的VBS脚本z中的过程函数如何应用
- WinCC脚本系统及动作、函数和触发器的概念
- 博途中WinCC VBS 脚本的基础用法
关闭
站长推荐 /1
- 免费加入500人工控微信群
- 100多个500人微信群等你加入
查看 »
| 免责声明| 本站介绍| 工控课堂
( 沪ICP备20008691号-1 || 沪公网安备 31010602005455号 )| 网站地图
GMT+8, 2021-11-12 06:46
, Processed in 0.055706 second(s), 38 queries
.
Powered by Discuz! X3.4
Copyright © 2001-2021, Tencent Cloud.
返回顶部
深圳SEO优化公司雨花台区网站优化信息推荐优化网站的方法很好火26星宁波江东区网站优化怎么样安庆企业网站优化哪家便宜莲花百度网站优化郑州瓷砖行业网站优化推广渠道门头沟快速优化网站大鹏网站优化哪家强淮北网站优化选哪家好北京网站优化推广收集桐城网站优化哪个品牌好网站手机优化显示广州市网站优化价格网站SEO优化代运营网站优化中搜索引擎索引的原理深圳整站优化网站制作软文推广天河公司网站优化推广价格网站内部优化术语如何优化网站内链网站SEO优化费用黄冈网站优化公司排名精简代码 优化网站昌平网站快速优化太原家装行业网站优化推广渠道石景山哪家网站优化好重庆网站优化推广南通企业网站优化网站自然排名优化方案金华网站优化厂家青岛网站优化词歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运
深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化
|