内存解析(stack,heap,static)

2024-05-12 22:48
文章标签 内存 stack heap 解析 static

本文主要是介绍内存解析(stack,heap,static),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

内存解析(stack,heap,static)

栈区(stack),由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.

堆区(heap),一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。
静态区(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。


在C/C++中,编译程序占用的内存分成5个部分,它们分别是堆(heap),栈(stack),全局/静态储存区(static) ,常量储存区和程序代码区。

:由编译器在需要时分配,在不需要的时候自动清除变量的储存区。比如程序中 int,float,char等原始类型定义变量时就是在栈上给变量分配空间,不需malloc也不需free,程序结束时,编译器会自动释放。

: 就是需要人为申请(malloc,new)的内存块,它们的释放编译器不会去管,所以需要我们手动释放(free,delete)。如果程序员没有释放掉,在程序结束后,操作系统会自动回收,但有弊端。

全局/静态储存区: 全局变量和静态变量被分配到同一块内存中,在C语言中,全局变量分为初始化和未初始化,在C++中没有区分,它们共占用一块内存。

常量储存区:这是比较特殊的储存区,全部储存的是常量字符串,只可读,不允许被修改(非正常手段除外)。

程序代码区:存放函数体的二进制代码。

这里最重要的是明确区分堆和栈,由一下几个方面来对比:

管理方式: 
对于栈来讲,是由编译器自动管理,无需我们手工控制; 
对于堆来说,释放工作由程序员控制,容易产生内存泄漏(memory leak)。 
地址比较: 
栈:在windows下,栈是向低地址扩展的数据结构,是一块连续的空间。 
堆:是向高地址扩展的数据结构,是不连续的。 
空间大小: 
堆大小: 一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。 
栈大小:但是对于栈来讲,在windows下,栈的大小是2M。根据编译器不同而不同,但一般都是1,2M,所以比起堆来非常小。当申请空间小于栈的剩余空间时,就会栈溢出。 
碎片问题: 
对于堆来讲:频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 
对于栈来讲:则不会存在这个问题, 因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出。 
分配效率: 
栈:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持,分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。 
堆:堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法,在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。 
储存内容: 
栈:在函数调用时,在大多数的C编译器中,参数是由右向左入栈的,然后是函数的局部变量。注意静态变量不能入栈。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后是栈顶指针指向函数的返回地址,也是主函数中的下一条指令的地址,程序由该点继续进行。 
堆:一般是在堆的头部用一个字节存放堆得大小。堆中具体内容由程序员安排。

static用来控制变量的存储方式和可见性 
函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。

需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。

static的内部机制: 
静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

static的优势: 
可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

引用静态数据成员时,采用如下格式: 
<类名>::<静态成员名> 
如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格来引用静态数据成员。

(1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。 
(2)不能将静态成员函数定义为虚函数。 
(3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针,函数地址类型是一个“nonmember函数指针”。 
(4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X Window系统结合,同时也成功的应用于线程函数身上。 
(5)static并没有增加程序的时空开销,相反它还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。 
(6)静态数据成员在<定义或说明>时前面加关键字static。 
(7)静态数据成员是静态存储的,所以必须对它进行初始化。 
(8)静态成员初始化与一般数据成员初始化不同: 
初始化在类体外进行,而前面不加static,以免与一般静态变量或对象混淆; 
初始化时不加该成员的访问权限控制符private,public等; 
初始化时使用作用域运算符来标明它所属类; 
所以我们得出静态数据成员初始化的格式: 
<数据类型> <类名>::<静态数据成员名>=<值> 
(9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们又重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling (C++对表示的不同变量或者函数的同一个标识符进行一种编码的方法使得得以区别开不同变量或者函数)用以生成唯一的标志。


这篇关于内存解析(stack,heap,static)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/983959

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro