C++内存地址分配和内存区划分简介

c++学习笔记(二十五)浅谈存储器和寄存器 什么是存储器? 存储器可分为主存储器(简称主存或内存)和辅助存储器(简称辅存或外存)两大类。和CPU直接交换信息的是主存。 存储器是许多存储单元的集合,按单元号顺序排列。每个单元由若干二进制位构成,以表示存储单元中存放的数值,这种结构和数组的结构非常相似,故在VHDL语言中,通常由数组描述存储器。速度上比寄存器稍慢,但容量大很多。 主存的工作方式是按存储单元的地址存放或读取各类信息,统称访问存储器。主存中汇集存储单元的载体称为存储体,存储体中每个单元能够... 阅读详情
第一部分 C++内存地址分配简介

1 内存地址是从高地址到低地址进行分配的:  

[cpp]  view plain copy
  1. int i=1;  
  2. int j=1;  
  3. cout<<&i<<endl<<&j<<endl;   //输出:0012FF60(高地址处) 0012FF54(低地址处)  

2 函数参数列表的存放方式是,先对最右边的形参分配地址,后对最左边的形参分配地址。

3 Little-endian模式的CPU对操作数的存放方式是从低字节到高字节的

0x1234的存放方式入下:

0X4000 0x34

0X4001 0x12

4 Big-endian模式的CPU对操作数的存放方式是从高字节到低字节的

0x1234的存放方式入下:

0x4000 0x12

0x4001 0x34

5 联合体union的存放顺序是所有成员都从低地址开始存放。

6 一个变量的地址是由它所占内存空间中的最低位地址表示的。

0X4000 0x34

0X4001 0x12

0x1234 的地址位0x4000

7 堆栈的分配方式是从高内存地址向低内存地址分配的。

int ivar=0;

int iarray[2]={11, 22};

注意iarray[2]越界使用,比如对其赋值

iarray[2]=0;

那么则同时对ivar赋值为0,可能产生死循环,因为它们的地址相同,即&ivar等于&iarray[2]。

 

第二部分 C/C++内存区划分

一 在C中分为这几个存储区
1.栈 由编译器自动分配释放;
2.堆 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收;
3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束释放;
4.另外还有一个专门放常量的地方。 程序结束释放。
                                                                                                                                              
在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。比如:

[cpp]  view plain copy
  1. int  a = 0; //全局初始化区  
  2. char *p1; //全局未初始化区  
  3. void main()  
  4. {       
  5. int b;                   //栈       
  6. char s[] = "abc"; //栈       
  7. char *p2;         //栈       
  8. char *p3 = "123456";       //123456{post.content}在常量区,p3在栈上       
  9. static int c = 0;          //全局(静态)初始化区       
  10. p1 = (char *)malloc(10);   //分配得来得10字节的区域在堆区       
  11. p2 = (char *)malloc(20);   //分配得来得20字节的区域在堆区       
  12. strcpy(p1, "123456");      //123456{post.content}放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块  
  13. }  

二.在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区
1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)。
在bbs上,堆与栈的区分问题,似乎是一个永恒的话题。   
首先,我们举一个例子: 
void f() 
{
    int* p=new int[5];
} 

这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?它分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。

        在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下: 

00401028 push 14h 
0040102A call operator new (00401060) 
0040102F add esp,4 
00401032 mov dword ptr [ebp-8],eax 
00401035 mov eax,dword ptr [ebp-8] 
00401038 mov dword ptr [ebp-4],eax
 

  这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。 

  好了,我们回到我们的主题:堆和栈究竟有什么区别? 

  主要的区别由以下几点: 

  1、管理方式不同; 

  2、空间大小不同; 

  3、能否产生碎片不同; 

  4、生长方向不同; 

  5、分配方式不同; 

  6、分配效率不同; 

  管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。 

  空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改: 

  打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。 

  注意:Reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。 

  碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在它弹出之前,在它上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。 

  生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。 

  分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,不需要我们手工实现。 

  分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高(我的注释:关于EBP寄存器请参考另一篇文章)。

        堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

  从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。 

  虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。 


  无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生意想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的:)


内存或寄存器定义和赋值 在嵌入式中,会经常遇到寄存器、内存的数据传输,如何向寄存器中写入数据呢?现举例说明:         #define rDISRC0 (*(volatile unsigned *)0x4b000000) //DMA 0 Initial source 这是s3c2410的DMA的初始源地址寄存器的定义。DISRC0是DMA0的初始源地址寄存器,s3c2410为其分配的地址为:0x4b00 阅读详情

相关推荐

C++ 指针和自由储存空间

指针可以理解为和int,double同样性质的数据类型,占8个字节(64位),储存一个地址。 运算符&可以获得一个数据的地址,运算符*应用于指针可以得到指针指向的地址储存的值(解除引用)。 int num = 100; int* p_num = &num; *p_num = *p_num + 1; cout << num << endl << p_num<<endl<<&num; 结果是 10

name_nai的博客 753

【C/C++】关于计算机内存地址的理解

经常可以看到某些书籍上写的内存地址0x0001,在另外一些书籍上写的内存地址又变成了0x00000001。都是表示的编号为1的内存地址,为什么一个是4位16进制表示,另外一个又是用8位16进制表示呢 根据不同的硬件环境来的。个人理解:有些CPU只能寻址16位(16根地址线),所以用4位十六进制表示地址就够用了。有些CPU只能寻址32位(32根地址线),所以用8位十六进制 计算机中存储器的容量...

xyz_ 7752

从JTAG到AS模式:一文搞懂EP4CE10E22C8N所有配置引脚(MSEL, nCONFIG, DATA0)的用法

本文深入解析EP4CE10E22C8N FPGA的配置引脚(MSEL, nCONFIG, DATA0)在不同模式下的使用方法,特别关注从JTAG到AS模式的切换要点。通过详细的配置模式对比、状态机分析和PCB布局建议,帮助开发者避免常见陷阱,提升FPGA配置成功率。

weixin_33713350的博客 320

C++内存地址分配与划分

内核:在一些系统中,当系统调用发生时,操作系统或者操作系统内核会编程应用程序内存的一部分。栈:栈中包含活动记录,其中包含当前活动函数调用的返回地址和局部变量等信息。共享库:为了动态链接共享库文件而创建的一个内存片段堆内存:被用作堆内存来使用和分配的一块内存空间。如果运行时需要一些可变大小的小内存块,那么这些内存就是从这个区域中分配的未初始化的数据: 没有初始化的全局变量被放在固定地址中。通常,这段

liuhuiyi的专栏 2539

2023西工大NOJ (C语言版) 完结!!!

代码均可AC,不含解析。

annesede的博客 8万+

时序约束实战:从setup/hold到时钟路径约束的深度解析

本文深度解析数字设计中的时序约束实战,重点探讨如何从setup/hold检查的核心原理出发,理解并应用时钟路径约束。文章通过剖析Path Requirement的计算逻辑,结合同频、倍频及异步时钟域等典型场景,提供了从解读时序报告到编写、调试SDC约束的系统方法,帮助工程师有效应对复杂SoC设计中的时序收敛挑战。

weixin_29291185的博客 105

C++ 内存区域划分

C++内存区域划分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其 操作方式类似于数据结构中的栈。 2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回 收 。 3、全局区(静态区)(static)— 全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相...

littleflypig的博客 442

Win11下WSL2安装Ubuntu22.04避坑指南:从镜像源到C++环境配置

本文提供了一份详细的Win11下WSL2安装Ubuntu22.04的实战避坑指南。内容涵盖从WSL2核心安装、自定义安装路径,到更换国内镜像源以解决更新缓慢问题,并一站式配置完整的C++开发环境(包括GCC、CMake、vcpkg等),最后分享提升开发体验的进阶技巧,助你在Windows上打造高效的Linux开发工作站。

weixin_29260291的博客 562

内存区域的划分和分配

一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。程序结束时由编译器自动释放。 2、堆区(heap) — 在内存开辟另一块存储区域。一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。用malloc...

昔拉的博客 739

Xilinx实战-深入AXI UART Lite IP核的配置与调试

本文详细介绍了Xilinx FPGA中AXI UART Lite IP核的配置与调试方法,包括Vivado环境下的IP核设置、关键参数优化、寄存器操作及常见问题排查。通过实战案例和代码示例,帮助开发者快速掌握AXI接口的串口通信技术,提升嵌入式系统开发效率。

weixin_29189377的博客 227

2025年最新雷电模拟器9.22安装Magisk面具和LSPosed框架完整图文教程 文章末尾附所需软件下载

操作环境:雷电模拟器9.0.22 虚拟系统版本android 91、设置-其他设置-Root权限-开启2、设置-性能设置-System.vmdk可写入3、安装Magisk-18a0b94a-delta(25205).apk,运行并给予root权限4、选择右上角安装弹出“允许“magisk Delta”访问您设备上的照片、媒体内容和文件吗?“选择允许,然后结束magisk进程。5、从新运行magisk选择右上角安装-选择修补boot镜像中的vbmeta和安装到Recovery。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 7102

C\C++内存区域划分比较

内存定义 硬件角度:内存是计算机必不可少的一个组成部分,是于CPU沟通的桥梁,计算机中所有的程序都是运行在内存中的。 逻辑角度:内存是一块具备随机访问能力,支持读、写操作,用来存放程序及程序运行中产生的数据的区域。 内存单位和编址 位 :( bit ) 是电子计算机中最小的数据单位。每一位的状态只能是0或1。 字节:1 Byte = 8 bit ,是内存基本的计量单位, 字:“字” ...

巧儿的博客 432

【推荐100个unity插件之7】使用BehaviorDesigner插件制作BOSS的AI行为树

Behavior Designer是一个行为树插件,是一款为了让策划,程序员,美术人员方便使用的可视化编辑器。Behavior Designer提供了强大的AP1可以让你轻松的创建tasks(任务),配合uScript和PlayMaker这样的插件,可以不费吹灰之力就能够创建出强大的Al系统,而无需写行代码。(不懂PlayMaker的,我后面在单独出文章介绍一下,可以期待一下)

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 6570

【JVM】内存区域划分,类加载机制和垃圾回收机制

本期讲解:JVM的内存区域划分、类加载机制、垃圾回收机制。

我的主页 5836

机器人视觉仿真软件:PCL (Point Cloud Library)_(8).点云分割技术

点云分割技术在机器人视觉处理中起着至关重要的作用,通过不同的分割方法可以有效地将点云数据中的不同物体或区域分离出来。PCL提供了丰富的分割算法,包括基于平面的分割、基于聚类的分割、基于模型的分割和基于条件的分割等。每种方法都有其适用的场景和优缺点,选择合适的方法可以显著提高点云处理的效率和准确性。希望本节的内容能够帮助读者更好地理解和应用点云分割技术。

kkchenjj的博客 822

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

(转载)C++内存地址分配

第一部分 C++内存地址分配简介 1 内存地址是从高地址到低地址进行分配的:   [cpp] view plaincopy int i=1;   int j=1;   cout//输出:0012FF60(高地址处) 0012FF54(低地址处)   2 函数参数列表的存放方式是,先对最右边的形参分配地址,后对最

Young的游戏舞台 1241

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

上一篇: 简述网络爬虫的系统实现
下一篇: SQLite vs MySQL vs PostgreSQL:关系型数据库比较
张哲BJUT
博客等级 码龄15年 151粉丝 111原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值