内存的堆分配和栈分配 & 字符数组,字符指针,Sizeof总结

2023-11-18

 

程序占用的内存分为几个部分,各个部分起什么作用,字符数组,字符指针在实现上有什么区别等等。本文对此做了详细阐述,特转载于此,供大家学习参考之用。

 


一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。-->分别是data区,bbs区 
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放-->coment区
5、程序代码区—存放函数体的二进制代码。-->code区

例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
二、堆和栈的理论知识
2.1申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。
2.2
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
2.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
2.6存取效率的比较
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaa是在运行时刻赋值的;
而bbbbbbbbbbb是在编译时就确定的;
但是,在以后的存取中,在栈上的数组指针所指向的字符串(例如堆)快。
比如:
#i nclude
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
对应的汇编代码
10: a = c[1];
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4],cl
11: a = p[1];
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
00401070 8A 42 01 mov al,byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4],al
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。
2.7小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
----------------------------------------------------------------------------------------------------------------------
全局变量或者静态变量,它们都放在堆里的
局部变量放在栈里的
堆区,也叫自由存储区.
为什么说在堆上分配内存比在栈上分配内存慢?堆空间的开辟需要用系统函数,栈上直接修改指针
堆空间的管理需要系统记帐,栈上的空间可以由编译器管理或是保存在某个处理器寄存器中。
堆空间的释放需要系统管理,栈上的释放可以直接丢弃。堆空间需要通过栈上的指针间接引用,所以访问会慢
记得在apue2上面看到关于线程中有这样一段话,大致意思是,一个线程有自己的堆栈,可以在堆栈上分配内存,比如说一个结构体,如果这个线程调用了pthread_exit()返回这个结构体指针的时候之后要特别的小心,因为很有可能这个结构体里面的成员值发生改变,这个可以理解,因为同一个进程所有线程的资源是共享的,当这个线程退出之后那部分以前用过的堆栈很可能被其它线程占用,但同时又说如果malloc就不会出现这样的问题,
比如,在栈上分一个int,只要esp-4就可以了,
在堆上系统要记录被分配内存的信息,以便释放
BTW:
栈有序
堆无序
----------------------------------
内存分配方式有三种:   
    
   1.从静态存储区域分配内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。   
    
   2.在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配内存容量有限。   
    
   3.从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
----------------------------------------
一般所说的堆栈(stack)往往是指栈,先进后出,它是一块内存区。用以存放程序的局部变量,临时变量,函数的参数,返回地址等。在这块区域中的变量的分配和释放由系统自动进行。不需要用户的参与。   
   而在堆(heap,先进先出)上的空间则是由用户进行分配,并由用户负责释放。
===================================================================================
字符数组字符指针,Sizeof总结
1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符,如在代码中写 "abc",那么编译器帮你存储的是"abc\0"
2."abc"是常量吗?答案是有时是,有时不是。
不是常量的情况:"abc"作为字符数组初始值的时候就不是,如
     char str[] = "abc";
因为定义的是一个字符数组,所以就相当于定义了一些空间来存放"abc",而又因为字符数组就是把字符一个一个地存放的,所以编译器把这个语句解析为
    char str[3] = {'a','b','c'};
又根据上面的总结1,所以 char str[] = "abc";的最终结果是 char str[4] = {'a','b','c','\0'};做一下扩展,如果char str[] = "abc";是在函数内部写的话,那么这里 的"abc\0"因为不是常量,所以应该被放在栈上。
是常量的情况: 把"abc"赋给一个字符指针变量时,如
     char* ptr = "abc";
   因为定义的是一个普通指针,并没有定义空间来存放"abc",所以编译器得帮我们找地方来放"abc",显然,把这里的"abc"当成常量并把它放到程序 的常量区是编译器 最合适的选择。所以尽管ptr的类型不是const char*,并且ptr[0] = 'x';也能编译 通过,但是执行ptr[0] = 'x';就会发生运行时异常,因为这个语句试图去修改程序 常量区中的东西。
记得哪本书中曾经说过char* ptr = "abc";这种写法原来在c++标准中是不允许的, 但是因为这种写法在c中实在是太多了,为了兼容c,不允许也得允许。虽然允许, 但是建议的写法应该是const char* ptr = "abc";这样如果后面写ptr[0] = 'x'的话编译器就不会让它编译通过,也就避免了上面说的运行时异常。
    又扩展一下,如果char* ptr = "abc";写在函数体内,那么虽然这里的"abc\0"被放在常量区中,但是ptr本身只是一个普通的指针变量,所以ptr是被放在栈上的, 只不过是它所指向的东西被放在常量区罢了。
3.数组的类型是由该数组所存放的东西的类型以及数组本身的大小决定的。 如char s1[3]和char s2[4],s1的类型就是char[3],s2的类型就是char[4], 也就是说尽管s1和s2都是字符数组,但两者的类型却是不同的。
4.字符串常量的类型可以理解为相应字符常量数组的类型, 如"abcdef"的类型就可以看成是const char[7]
5.sizeof是用来求类型的字节数的。如int a;那么无论sizeof(int)或者是sizeof(a)都 是等于4,因为sizeof(a)其实就是sizeof(type of a)
6.对于函数参数列表中的以数组类型书写的形式参数,编译器把其解释为普通 的指针类型,如对于void func(char sa[100],int ia[20],char *p) 则sa的类型为char*,ia的类型为int*,p的类型为char*
7.根据上面的总结,来实战一下:
     对于char str[] = "abcdef";就有sizeof(str) == 7,因为str的类型是char[7], 也有sizeof("abcdef") == 7,因为"abcdef"的类型是const char[7]。
      对于char *ptr = "abcdef";就有sizeof(ptr) == 4,因为ptr的类型是char*。
      对于char str2[10] = "abcdef";就有sizeof(str2) == 10,因为str2的类型是char[10]。
      对于void func(char sa[100],int ia[20],char *p); 就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4, 因为sa的类型是char*,ia的类型是int*,p的类型是char*

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内存的堆分配和栈分配 & 字符数组,字符指针,Sizeof总结 的相关文章

  • 无法在 XAMPP 和 Windows XP 上安装 Xdebug

    我知道 这个问题已经被问过好几次了 但答案并没有解决我的问题 我在 Windows XP SP3 上运行 XAMPP 1 8 2 并且在安装 XDebug 时遇到困难 我从网站下载了XDebug 不幸的是 安装向导对我不起作用 我的 PHP
  • 在 Mercurial 中配置 eol 扩展时出现问题

    我正在尝试将 UNIX 托管的 Mercurial 存储库克隆到 Windows 计算机 我希望使用eol扩展名 以便我在服务器上以 LF 结尾的文本文件在克隆到 Windows 计算机时具有 CRLF 以前我用过win32文本扩展效果很好
  • 使用 javaw.exe 启动应用程序时,不会执行关闭挂钩

    如果我使用 javaw exe 启动 Java 应用程序 则当用户从其 Windows 帐户注销时 不会执行关闭挂钩 该应用程序实际上是使用 launch4j 生成的 exe 文件启动的 但我知道它使用 javaw exe 来启动它 这似乎
  • 如何从 Python (2.7) 中生成的进程中消除 Windows 控制台? [复制]

    这个问题在这里已经有答案了 可能的重复 在没有控制台的情况下使用 Popen 在 python 中运行进程 https stackoverflow com questions 1813872 running a process in pyt
  • 从 IPConfig 获取 IP 地址,稍后在代码中使用,或保存 [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 是否可以运
  • 如何根据操作系统系列拥有不同的依赖关系

    我正在编写一个跨平台库 它具有特定于平台的依赖关系 一个用于类 UNIX 平台 一个用于 Windows 这些板条箱仅在特定平台上编译 因此我不能正常地将它们全部添加到依赖项下 在我实际使用的 Rust 代码中cfg属性 例如 cfg un
  • 动态获取当前LDAP路径

    我正在使用 C 和 NET Framework 4 0 开发一个库 我想检索所有活动目录用户 它效果很好 但我的问题是 如果我在另一个域上运行我的程序 我必须更改此设置 private static string ldapPath LDAP
  • .NET 中的 vista/win7 放大 API

    Win7 Vista SDK中有magnification lib可供使用放大API http msdn microsoft com en us library ms692162 28VS 85 29 aspx对于C 如何在 NET win
  • XP 和 Vista 之间使用 Winsock API 的 UDP 多播差异

    Windows XP 和 Windows Vista 之间设置 UDP 多播套接字所需的实现似乎有所不同 具体来说 在 Windows XP 下 您must call bind 在您可以引用任何特定于多播的套接字选项之前 然而 在windo
  • Windows 服务和 Windows 进程有什么区别?

    是什么不同之处窗户之间service和一个窗户process 服务是真正的 Windows 进程 没有区别 服务的唯一特殊之处在于它由操作系统启动并在单独的会话中运行 一种独立的方式 可防止其干扰桌面会话 传统上命名为daemon http
  • 如何使用 WEBrick 执行 Ruby 代码而不是将代码转储到浏览器?

    当我在浏览器中运行程序时遇到问题WEBrick服务器 http ruby doc org stdlib 2 1 1 libdoc webrick rdoc WEBrick html 它显示了 2loop rb 文件中编写的代码 当我跑步时r
  • Shell 执行将窗口置于前面

    我正在使用此函数从我的 MSI 调用可执行文件 然而 可执行文件的窗口隐藏在我的 MSI 窗口后面 有什么办法可以把它带到前面吗 我尝试在调用之前最小化所有窗口ShellExecute但这仍然没有将可执行窗口带到前面 extern C UI
  • 使用 Composer 安装 PHPUNIT

    我有一个关于 Symfony 2 的项目 我想在 Windows 7 上使用 PHPUNIT On githut phpunit is Composer Simply add a dependency on phpunit phpunit
  • 无法以管理员身份更改 Windows 10 中的系统变量路径

    我正在 Windows 10 计算机中安装 Java JDK 需要通过在 Windows 设置中编辑系统变量来注册其路径 但我以管理员和普通用户身份尝试过 但它不允许我添加路径 甚至复制粘贴或仅添加任何角色 在 Win 8 中执行此操作没有
  • Windows 通用应用程序串行端口无法打开,SerialDevice.FromIdAsync 始终为 null

    我正在尝试在 Windows 通用应用程序中使用串行端口 我一直在使用微软的串行示例应用程序作为模板 但是我遇到了一个相当奇怪的问题 var dis await DeviceInformation FindAllAsync SerialDe
  • 如何安装适用于 Windows C++ 的最新版本 OpenGL?

    我正在使用 Visual Studio 2010 运行 Windows 7 包含的 OpenGL 版本 include 是版本 1 1 我希望使用合理的当前版本 某种版本 3 或 4 我需要做什么才能达到该状态 OpenGL SDK 页面位
  • Powershell脚本运行带有参数的exe文件

    我需要脚本来运行带参数的 exe 文件 我就是这么写的 请问有更好的方法吗 Command Networkpath Restart exe Parms t 21600 m 360 r f Prms Parms Split Command P
  • python:Windows终端中的unicode,使用的编码?

    我在 Windows 7 终端中使用 Python 解释器 我正在尝试了解 unicode 和编码 I type gt gt gt s gt gt gt s x89 gt gt gt u u gt gt gt u u xeb 问题1 字符串
  • 如何开始使用“scipy”

    我之前安装过 Python 3 4 2 和 3 5 2 在这两种情况下 我都可以在 Idle 中涉足编写和测试代码 这给了我两个窗口 一个用于代码的 运行 窗口 一个用于交互和测试的 Shell 窗口 输出 抱歉 不确定术语是否正确 现在我
  • DISM.exe 返回代码?

    我有一个程序调用 dism exe 程序 它在后台运行一些命令 现在 我只检查返回代码 0 或其他任何内容 以显示进程失败或成功 我可以用什么来交叉检查返回代码以获得准确的返回错误 DISM 参考了哪些回报 评论中提供的链接DISMAPI

随机推荐

  • 轻盈绘图的Excalidraw插件

    朋友们 你们的Obsidian彩虹屁狂魔重新上线 从这篇Obsidian实践开始 我想跟大家陆续分享一下 Obsidian中那些让人眼前一亮的宝藏插件 原本我想从逻辑上最基础 使用上最简单的插件讲起 然鹅 请原谅我实在按捺不住自己迫切的心情
  • JAVA小游戏(国王和大臣)

    棋规则和需求 规则 棋子 2颗国王棋和 8 16 颗大臣棋 分别用黑白两色代表国王和大臣 黑子为国王 白子为大臣 棋盘 5 9的二维棋盘 共37个交点 如图所示 吃子规则 包括移子和添子 1 国王每回合可以选择移动一步 或者跳吃大臣一颗棋子
  • ceph集群警告和错误类型

    指标 说明 级别 noscrub flag s set 防止集群做清洗操作 full flag s set 使集群到达设置的full ratio值 会导致集群阻止写入操作 nodeep scrub flag s set 防止集群进行深度清洗
  • Vue与WebGL结合

    本文将WebGL与当前前端比较火的Vue框架进行初步结合 以备后续项目开发需要 部分代码源自https blog csdn net GISuuser article details 82224057以及 WebGL编程指南 大家可以自己百度
  • 使用IDEA创建springboot项目时,出现错误Cannot download ‘https://start.spring.io‘: connect timed out

    使用IDEA创建springboot项目时 出现错误Cannot download https start spring io connect timed out 解决方案 1 选择 custom 并使用阿里云 https start al
  • Objective-C Runtime 1小时入门教程

    原文出处 ian ianisme 一 前言 如果你没有Objective C基础 请学习了基础的iOS开发再来 这个1小时是给有一定iOS基础的童鞋的 如果你是大牛或者你感觉Objective C Runtime太简单不用1小时学习的 也请
  • MVVM框架极简总结

    MVVM是一个前端框架 由model view viewmodel组成 是MVC的升级版 变成了由viewmodel进行m和v之间的双向数据绑定 虽然代码量相较于MVC变得更多了一点 逻辑和视图分开来写 但是更利于代码解耦 也更方便测试来自
  • 刷脸支付互联网思维的推广让传播变得更快

    行业巨头也在加速抢占刷脸支付的风口 支付宝近期发布了新的刷脸支付设备 同时宣布补贴力度不设上限 以此来加速刷脸支付的落地 在巨头们的大力推动下 刷脸支付普及率不断提升 最新数据显示 目前我国刷脸支付用户数已经破亿 其中 90后 占据了主流
  • Qt之软键盘的实现

    文章目录 前言 一 基于中文汉字数据库 1 核心代码 2 效果 二 基于谷歌拼音输入引擎 1 核心代码 2 效果 前言 Qt5 8版本开始推出了基于QML实现的软键盘功能 在此之前 并没有官方版本的软键盘 本篇主要介绍Qt实现软键盘的两种方
  • conda创建虚拟环境 python版本不对_如何在conda虚拟环境中指定python版本

    我在新的职位上负责一个工作项目 我相信虚拟环境是在它里面创造的 正如我所见 head bm3 py usr bin env opt bm3 venv bin python3 bm3 venv是使用requirements txt 使用vir
  • 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由

    关注微信公众号 芋道源码 有福利 1 RocketMQ MyCAT Sharding JDBC 所有源码分析文章列表 2 RocketMQ MyCAT Sharding JDBC 中文注释源码 GitHub 地址 3 您对于源码的疑问每条留
  • c++学习之pair对组创建

    功能描述 成对出现的数据 利用队组可以反回两个数据 函数原型 pair
  • C# 项目没有.sln文件的解决办法:

    什么是sln文件 sln文件开发中使用的解决方案文件 使用解决方案文件 后缀为sln的文件 表示一个项目组 他通常包含一个项目中所有的工程文件信息 包括文件版本 工程信息 全局设置 通过打开sln文件就可以加载整个项目 但是我新建的一个项目
  • Win11总是出现BitLocker恢复,想要彻底关闭它该如何操作?

    win11解除bitlocker加密方法一 1 首先按下键盘 Win R 打开运行 如图所示 2 打开运行后 在其中输入 control 并点击 确定 打开控制面板 如图所示 3 打开后 进入 bitlocker驱动器加密 如图所示 4 随
  • 【计算机视觉

    文章目录 一 ResNeSt 二 ShuffleNet v2 三 FBNet 四 Inception v4 五 ResNet D 六 MetaFormer 七 PyramidNet 八 RevNet 九 Convolutional Visi
  • Shell脚本运行中的停止方法

    Linux系统Shell中提交了一个脚本 但是需要停止这个进程 如何处理 方式1 killall file flume kafka 说明 killall是一个命令 不是kill all file flume kafka是脚本名 此方法简单粗
  • C++ 发送http太慢导致数据积压问题分析(Linux)

    问题现象 程序接收数据 对数据进行简单地处理 处理完之后发送到其他平台 程序在接收到数据到在其他平台上面显示时间差了将近一个小时 存放要发送到其他平台的数据的容器积压了大概一百五十多万条的数据 问题排查过程 1 接收数据与发送数据到其他平台
  • 列出所有共享的文件夹权限或 NTFS 权限 (PowerShell)

    列出所有共享的文件夹权限或 NTFS 权限 PowerShell 此 PowerShell 脚本说明如何列出所有共享的文件夹权限或 NTFS 权限 下载 ListAllSharedFolderPermission zip 出所有共享的文件夹
  • HTML5网页设计常用标记-链接标记和列表标记

    链接标记 在HTML语言中 利用 a 标记在网页中创建超链接 语法格式 a href 跳转目标 target self 文本或图像 a 文字设置为超链接后 默认显示为加下划线的蓝色字体 图片设置为超链接后 会自动加一个黑色的边框 a 标记常
  • 内存的堆分配和栈分配 & 字符数组,字符指针,Sizeof总结

    程序占用的内存分为几个部分 各个部分起什么作用 字符数组 字符指针在实现上有什么区别等等 本文对此做了详细阐述 特转载于此 供大家学习参考之用 一个由C C 编译的程序占用的内存分为以下几个部分 1 栈区 stack 由编译器自动分配释放