[程序设计语言] 堆和栈的全面总结

操作系统堆栈:

        分配由编译器自己主动和自己主动释放。对应于堆栈的函数。参数存储功能值、函数调用结束后完成值和局部变量的函数体内。段内存空间。其操作和组织方式与数据结构中的栈十分相似。栈是为了运行线程留出的内存空间。当调用函数时创建栈。当函数运行完毕,栈就被回收了。

操作系统中的堆:

         由程序猿手动进行内存的申请与释放。因为程序猿手动申请及释放的内存块存放在堆中。堆中有非常多内存块,所以堆的组织方式类似于链表。操作系统中的堆与数据结构中的堆全然不同。我认为通俗的理解能够是这种:数据结构中的堆是"结构堆"。有严谨的逻辑和操作方式。而操作系统中的堆。更像是使用链表将"一堆杂乱的东西"联系起来。堆是为动态分配预留的内存空间,其生命周期为整个应用程序的生命周期。

当应用程序结束以后,堆開始被回收。

        每个线程都有一个属于自己的栈,但每个应用程序通常仅仅有一个堆(一个应用程序使用了多个堆的情况也是有的)。当线程被创建的时候。设置了栈的大小。

在应用程序启动的时候,设置了堆的大小。栈的大小一般是固定的,可是堆能够在须要的时候进行扩展,如程序猿向操作系统申请很多其它内存的时候。

因为栈的工作方式类似于数据结构中的栈,堆的工作方式类似于链表,所以栈显然会比堆快得多。依照栈的存取方式,想要释放内存或是新增内存。仅仅须要对应移动栈顶指针就可以。堆则要首先在内存的空暇区域寻找合适的内存空间,然后占用。然后指向这块空间。显然堆比栈要复杂得多。

       接下来本来是想将栈和堆分开进行陈述,斟酌了一下还是决定从同一方面对栈和堆进行比較。有了比較才明显。

1. 在创建栈的时候栈的大小就固定了,由于栈要连续占用一段空间。依据上文所属的堆的特性,决定了堆的大小是动态的,其分配和释放也是动态的。

2. 栈中的数据过多会导致爆栈。比方dfs写搓了。

而假如堆也爆了的话。。

。那说明内存也爆了。

3. 每一个函数的栈都是各自独立的,可是一个应用程序的堆是被全部的栈共享。

既然提到共享,那么这里就有"并行存取"的问题了。实际上并行存取是由堆控制的。而不是被栈控制的。

4. 栈的作用域仅限于函数内部,栈在函数结束的时候会自行释放掉空间。可是创建于堆上的变量必需要手动释放。堆中的变量不存在作用域的问题。由于堆是全局的。

5. 栈中存放的是函数返回值地址、函数參数。函数内的局部变量等。堆中存放的是由程序猿手动进行申请的内存块(malloc、new等)。

6. 堆和栈都按需进行分配。栈有严格的容量上限。而堆的容量上限则是"不严格"的。堆并没有固定的容量上限,它与当前的剩余内存量有关(事实上还不准确,操作系统还有虚拟内存或其它概念,所以堆的工作方式较为抽象)。


7. 通过移动栈顶指针就可以实现栈内存的分配。

在堆上分配内存的做法则是从当前空暇的内存中找一块满足大小的区域。就像链表的工作方式一样。

8. 仅仅要没有超出栈容量。栈能够进行随意的释放和申请内存。并不会造成内存出现故障,是安全的。而堆不同,大量申请和释放小内存块可能会造成内存问题,这些小的内存块零散的分布在内存中。导致兴许大块的内存申请失败。由于尽管空暇的内存足够多。可是并不连续。这样的情况下的小块内存叫做"堆碎片"。只是这并非什么大问题。详细详见"操作系统"的有关知识。

9. 栈在确定了栈底地址后,其栈顶指针从栈底地址開始。逐渐向低地址走。也就是说栈的存储空间是从高地址走向低地址的。

堆则相反,堆在申请空间的时候通常逐渐往高地址的方向来寻找可用内存。

纯粹的文字描写叙述显得枯燥无味,我们来看一些代码:

#include <iostream>
using namespace std;void func()
{int i = 5;int j = 3;int k = 7;int *p = &i;printf("%d\n", *p);printf("%d\n", *(p-1));printf("%d\n", *(p-2));}int main()
{func();getchar();return 0;
}
上述代码的结果是:5 3 7

从结果中我们能够看出两件事:

一是栈地址是连续的,我们能够通过一个指针和一个相对的大小,来"偏移"到别的变量上去。

二是从中能够看出栈地址是从高到低分布的,栈底在高地址。朝低地址的方向生长。

所以程序中是p-1而不是p+1。

void func()
{int *p = NULL;// 上行代码是个重点。这个指针待会会用于申请新的内存。// 此时除了它自身作为一个变量须要占用4字节的空间(指针都占4字节),没有不论什么其它空间被申请。// 这个指针变量是函数的局部变量,所以它被创建在栈上。int num = 100; 		// 这个变量相同创建于栈上。int buffer[100];	// 相同的,buffer占用了栈的400字节的空间p = new int[100];	// 注意,程序猿手动申请了一块空间,这400字节的内存创建于堆上。// 所以此刻p的状态是:p为函数局部变量,它指向了一块全局范围的内存空间。
}
// 函数体结束。上述函数有个严重的问题,那就是指针p的内存泄露。
// 正确的做法是在函数最后delete掉这块内存。或是返回这块内存的地址以供继续使用。
接下来我们来了解一下当调用一个函数的时候所发生的事情:
首先操作系统为这个函数分配了一个栈。由于在调用完这个函数以后须要能正确返回到下一条语句并继续运行,所以第一步是将调用完函数的下一条指令的地址压入栈。这样当函数调用完毕,栈顶指针一点点释放内存以后。栈顶指针指向了这个地址,就能返回到正确的位置继续运行了。

int main()
{func();printf("%d\n", 100);return 0;
}
比方上述代码,在调用func之前,首先把func的下一条语句,也就是printf语句的地址。存在栈中。

这样函数调用完毕后就能正确返回到这个printf并继续往后运行了。

注意这里的地址是指令地址,而不是变量地址什么的。它有那么点类似于操作系统中的程序计数器(PC,即Program Counter)。

然后把实參从右到左的顺序依次入栈(大多数的C/C++编译器为从右到左)接着是函数中的各种局部变量。要注意的是函数中的static变量是不入栈的。

全局变量和static变量在编译的时候就已经在静态存储区分配好内存了。


假设这个时候该函数又调用了其他函数,过程也是一样的。首先是返回地址,然后是參数和局部变量。

这样在每层调用结束,栈顶指针不断下降(释放内存)的时候,就能正确返回到之前调用的位置并继续往下运行了。
出栈。或者说释放内存的过程。依据栈的特性,是相反的,所以就不赘述了。


一个 C或C++程序,它眼中的内存地址分分为这么五个区域:

栈区(stack)、堆区(heap)、全局静态区(static)、文字常量区和程序指令区。

栈区和堆区前面已经介绍过。全局静态区用于存放全局变量和静态static静态变量。全局静态区分为两块内容:一块用于初始化以后的全局变量和静态变量,一块用于未初始化的全局变量和静态变量。全局静态区和堆一样,程序结束后由操作系统进行释放。文字常量区用于存放常量字符串。程序结束后由操作系统进行释放。

程序指令区最好理解,就是存放程序代码的二进制指令。

int cnt;		// 存放在全局静态区的未初始化区
int num = 0;	// 存放在全局静态区的已初始化区
int *p;			// 存放在全局静态区的未初始化区
int main()
{int i, j, k;			// 存放在栈区int *pBuffer = (int *)malloc(sizeof(int) * 10);	// 指针pBuffer在栈中,该内存在堆中char *s = "hactrox";	// 指针s存放在栈中,字符串存放在文字常量区中char str[] = "hactrox";	// str和字符串存放在栈中static int a = 0;		// a存放在全局静态区的已初始化区
}

char *s = "hactrox";    // "hactrox"在文字常量区。s指向这个区域中的"hactrox",所以这能够理解为。首先在文字常量区创建了这个字符串,然后s指向这个字符串这样两个步骤。

s本身作为一个局部变量存储在栈中。
// 以下的代码是错误的,指针还没指向就直接赋值了?
int *p = 5;
// 以下的代码才是正确的,首先要创建这个int型变量,然后p指向这个变量。new来的int变量在堆中。
int *p = new int(5);
接下来我们看一看一个很常见的问题:下述代码有没有什么问题?有问题的话问题在哪里?

#include <iostream>
using namespace std;char* f1()
{char *s = "hactrox";return s;
}char* f2()
{char s[] = "hactrox";return s;
}int main()
{printf("%s\n", f1());printf("%s\n", f2());getchar();return 0;
}
问题在于第二个函数,f2并不能正确返回那个字符串。在函数f1中。"hactrox"字符串创建于文字常量区。然后返回该常量字符串的地址,由于文字常量区的字符串是全局的,尽管指针s是局部变量,可是s在消亡前已经把目标地址送出来了。所以s消亡与否不是重点。重点是返回的地址所指向的区域还在,所以能正确显示。在函数f2中。“hactrox”与s均为局部变量。它们保存在栈中。

尽管s相同返回了一个地址。但这个地址所指向的内存已经被释放掉了。地址有效,但目标已无效。所以输出的仅仅是乱码。
关于文字常量区另一些东西要说(好像和本博客的主题扯远了?)

#include <iostream>
using namespace std;void func()
{char *str1 = "123";printf("%x\n", str1);char *str2 = "123";// 同在文字常量区。编译器可能会将str2直接指向str1所指向的内存。// 而不是开辟新的空间来存放第二个同样字符串。// 通过打印str2的指针可验证printf("%x\n", str2);char *s1 = "hactrox";printf("%x\n", s1);char *s2 = "hactrox";printf("%x\n", s2);
}
int main()
{func();getchar();return 0;
}
char s[] = "hactrox";
char *s = "hactrox again";
第二段代码,即文字常量区变量在编译的时候就已经确定了,
而第一段代码。是在执行的时候进行赋值的。
这样看起来貌似第二段代码的效率要高。事实上不然,
当在执行时刻用到这两个变量的时候,对于第一段代码。直接读取字符串,而对于第二段代码,首先读取该字符串指针。然后依据指针再读取字符串,显然效率就下降了。

事实上我认为关注栈和堆,事实上主要是关注作用域、生命周期和有效性的问题。

指针被释放了。不代表指针指向的内存会被释放。相同的。指针指向的内存被释放了,不代表指针会被同步释放或自己主动指向NULL,指针依然指向那块已经失效了的地址。这个地址不能用于。没有人能保证较长的有效地址,接下来会发生什么。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

转载于:https://www.cnblogs.com/bhlsheji/p/4675742.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/397772.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

py文件的操作

文件操作基本流程。 计算机系统分为&#xff1a;计算机硬件&#xff0c;操作系统&#xff0c;应用程序三部分。 我们用python或其他语言编写的应用程序若想要把数据永久保存下来&#xff0c;必须要保存于硬盘中&#xff0c;这就涉及到应用程序要操作硬件&#xff0c;众所周知&a…

CentOS系统启动流程你懂否

一、Linux内核的组成相关概念&#xff1a;Linux系统的组成部分&#xff1a;内核根文件系统内核&#xff1a;进程管理、内存管理、网络协议栈、文件系统、驱动程序。IPC(Inter-Process Communication进程间通信):就是指多个进程之间相互通信&#xff0c;交换信息的方法。Linux I…

怎样用css设置图片下的投影,css – 做这种投影的最佳方法是什么?

如果您更喜欢使用CSS来创建该类型的阴影,则可以将CSS3用作seen here!CSS/* Lifted corners */.lifted {-moz-border-radius:4px;border-radius:4px;}.lifted:before,.lifted:after {bottom:15px;left:10px;width:50%;height:20%;max-width:300px;-webkit-Box-shadow:0 15px 10p…

mysql 排版 指令_Mysql语句排版

SQL 高效排版指北统一 SQL 排版的相关用法&#xff0c;极大提高编写和维护 SQL 的效率。注: column 选取的字段&#xff1b;table 选取的表名语句结构错误SELECT column1 FROM table1 ORDER BY column1正确SELECTcolumn1FROMtable1ORDER BYcolumn1解析SQL 语句在内部执行时会…

Linux命令学习手册-tr命令 2015-07-26 20:35 9人阅读 评论(0) 收藏...

tr [OPTION]... SET1 [SET2] [功能] 转换或者删除字符。 [描述] tr指令从标准输入设备读取数据&#xff0c;经过字符串转译后&#xff0c;输出到标准输出设备。 通过使用 tr&#xff0c;您可以非常容易地实现 sed 的许多最基本功能。您可以将 tr 看作为 sed 的&#xff08…

css商品,商品标签例子——CSS3 transform 属性

积累很重要。从此开始记录前端生涯的点滴....div{width:150px;height:30px;background-color:#f83944;/* Rotate div */transform:rotate(-40deg);-ms-transform:rotate(-40deg); /* Internet Explorer */-moz-transform:rotate(-40deg); /* Firefox */-webkit-transform:rotat…

The literal of int xxxxx is out of range

有时候我们定义了long型的变量&#xff0c;当我们给该变量赋值过长的整数时&#xff0c;系统依然会提示长度超过范围&#xff0c;解决办法如下&#xff1a; long timeShow 1437565243495L; 我们需要在整形变量的后面加上“L”&#xff0c;便可以避免系统报错。转载于:https://…

debian 访问 windows 共享_【续】windows环境redis未授权利用方式梳理

01Redis未授权产生原因1.redis绑定在0.0.0.0:6379默认端口&#xff0c;直接暴露在公网&#xff0c;无防火墙进行来源信任防护。2.没有设置密码认证&#xff0c;可以免密远程登录redis服务02漏洞危害1.信息泄露&#xff0c;攻击者可以恶意执行flushall清空数据2.可以通过eval执行…

HTML比较常用的标签

1.全局架构标签&#xff1a;<html><head><title>标题</title><meta charset"utf-8"></head><body>正文部分</body></html><!--注释部分-->2.body标签的属性bgcolor&#xff1a;背景色text:整个网页的颜…

sae项目服务器,基于SAE的游戏服务器: Server on SAE for RGSS Games 部署在SAE上的简易游戏服务器,为用 RMXP/VX/VA 开发的游戏提供网络服务...

本项目已经关闭服务端已经关闭并且不再重启&#xff0c;后续请访问 RGSOS on Gitlab基于SAE的游戏服务器重写服务端逻辑中……暂时无法正常提供服务功能数据库封装封装了 SAE 上的 Memcached&#xff0c;KVDB 和 Storage 到 SAE_IO 类&#xff0c;并引申到两个子类&#xff1a;…

1090 Highest Price in Supply Chain (25)

A supply chain is a network of retailers&#xff08;零售商&#xff09;, distributors&#xff08;经销商&#xff09;, and suppliers&#xff08;供应商&#xff09;-- everyone involved in moving a product from supplier to customer. Starting from one root suppli…

mysql 列数据显示转成行数据显示_Mysql的列修改成行并显示数据的简单实现

创建测试表&#xff1a;DROP TABLE IF EXISTS test;CREATE TABLE test (year int(11) DEFAULT NULL,month int(11) DEFAULT NULL,amount double DEFAULT NULL) ENGINEInnoDB DEFAULT CHARSETutf8;插入数据&#xff1a;INSERT INTO test VALUES (1991, 1, 1.1);INSERT INTO test…

Android两种常见错误(ANR和FC)解决办法

ANR(Activity Not Respone)(无响应)先介绍下Main线程&#xff08;也称为UI线程、主线程&#xff09;功能: 1.创建UI控件2.更新UI控件状态3.事件处理限制&#xff1a;Main线程不建议有超过5秒的事件出现条件&#xff1a;当用户输入事件5s内没有得到响应&#xff0c;将弹出ANR对话…

mysql命令(command)

连接mysql命令: mysql -h 192.168.1.1 -P 3306 -uuserName -pPassword 显示表的索引: SHOW INDDEX FROM table_name 查看mysql的超时时间&#xff1a;SHOW GLOBAL VARIABLES LIKE %timeout% 备份表结构和表数据&#xff1a;mysqldump -u用户名 -p 库名 表1 表2 > xxx.sql只…

微信5.0登录提示服务器繁忙,iOS集成友盟社会化分享微信无法登录?

iOS集成友盟社会化分享SDK-5.0点击微信登录的时候出现无法获取accessToken的现象&#xff0c;其他如QQ、微博都可以正常登录使用。另外QQ、微博和微信分享都可以正常使用。望各位早日帮我解决或者分析一下。谢谢//微信登录之后的回调- (BOOL)application:(UIApplication *)appl…

sql获取某列出现频次最多的值_业务硬核SQL集锦

戳上方蓝字关注我 这两年学会了跑sql&#xff0c;当时有很多同学帮助我精进了这个技能&#xff0c;现在也写成一个小教程&#xff0c;反馈给大家。适用对象&#xff1a;工作中能接触到sql查询平台的业务同学(例如有数据查询权限的产品与运营同学)适用场景&#xff1a;查询hive&…

void ,NULL与0的区别联系

void ,NULL及0的区别联系 void的详解: void的字面意思是“无类型”或“空类型”&#xff0c;void*则为“无针型指针”&#xff0c;那就意味着void*可以指向任何类型的数据。 众所周知&#xff0c;如果指针p1和p2的类型相同&#xff0c;那么我们可以直接在p1和p2间互相赋值&…

python 2 days

1&#xff0c;格式化输出&#xff0c;%s %d 2&#xff0c;复习昨日讲题 编译型&#xff1a; 将代码一次性全部编译成二进制&#xff0c;然后运行。 优点&#xff1a;执行效率高。 缺点&#xff1a;开发效率低&#xff0c;不能跨平台。 C解释型&#xff1a; 代码…

nginx编译安装与配置使用

第一部分----nginx基本应用源码编译安装nginx1、安装pcre软件包&#xff08;使nginx支持http rewrite模块&#xff09;yum install -y pcre yum install -y pcre-devel2、安装openssl-devel&#xff08;使nginx支持ssl&#xff09;yum install -y openssl-devel3、创建用户ngin…

ubuntu+查看服务器文件夹权限,Ubuntu - 文件夹权限查看与修改

Ubuntu 文件的归属身份有四种&#xff1a;u - 拥有文件的用户(所有者)g - 所有者所在的组群o - 其他人(不是所有者或所有者的组群)a - 每个人或全部(u, g, o)1. 查看文件/文件夹权限ls -l filename # 查看文件权限ls -ld folder # 查看文件夹权限输出结果如&#xff1a;drwxrwx…