堆变量&栈变量

本文深入解析了程序内存分配中的堆和栈,包括它们的工作原理、分配方式、申请大小的限制及存取效率对比等内容,帮助读者理解两者之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

全局,静态,new产生的变量都在堆中
动态分配的变量在堆中分配  
  局部变量在栈里分配
函数中声明的变量在栈中  
  用了new标示符在堆中  
  全局变量和static变量都在全局区
程序为栈变量分配动态内存,在程序结束时为栈变量分配的空间将自动释放;而为堆变量分配的空间则不会自动释放,若在程序中没有没有释放堆变量,它将一直占用系统内存。
堆栈是一种执行“后进先出”算法的数据结构。

设想有一个直径不大、一端开口一端封闭的竹筒。有若干个写有编号的小球,小球的直径比竹筒的直径略小。现在把不同编号的小球放到竹筒里面,可以发现一种规律:先放进去的小球只能后拿出来,反之,后放进去的小球能够先拿出来。所以“先进后出”就是这种结构的特点。

堆栈就是这样一种数据结构。它是在内存中开辟一个存储区域,数据一个一个顺序地存入(也就是“压入——push”)这个区域之中。有一个地址指针总指向最后一个压入堆栈的数据所在的数据单元,存放这个地址指针的寄存器就叫做堆栈指示器。开始放入数据的单元叫做“栈底”。数据一个一个地存入,这个过程叫做“压栈”。在压栈的过程中,每有一个数据压入堆栈,就放在和前一个单元相连的后面一个单元中,堆栈指示器中的地址自动加1。读取这些数据时,按照堆栈指示器中的地址读取数据,堆栈指示器中的地址数自动减 1。这个过程叫做“弹出pop”。如此就实现了后进先出的原则。

堆栈是计算机中最常用的一种数据结构,比如函数的调用在计算机中是用堆栈实现的。
堆栈可以用数组存储,也可以用以后会介绍的链表存储。
下面是一个堆栈的结构体定义,包括一个栈顶指针,一个数据项数组。栈顶指针最开始指向-1,然后存入数据时,栈顶指针加1,取出数据后,栈顶指针减1。

#define MAX_SIZE 100
typedef int DATA_TYPE;
struct stack
{
DATA_TYPE data[MAX_SIZE];
int top;
};

在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多.
  int  a;  
  void  func(int arg)  
  {  
        static int b;  
        int  c;  
        void  *p;  
        p   =   (void *)malloc(100);  
  }  
   
  a在文件中声明是全局变量  
  b虽然在函数内声明,但是静态的,所以也在全局数据区  
  c,p是函数内声明,是栈变量  
  arg是参数,是栈变量  
  p指向的空间用malloc分配,是堆空间
 
 
 
一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

二、例子程序
这是一个前辈写的,非常详细
//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小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

堆和栈的区别主要分:
操作系统方面的堆和栈,如上面说的那些,不多说了。
还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。
虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。
 
 
<think>嗯,用户问的是ABAP变量还是存储的问题。这个问题涉及到编程语言的内存管理机制,需要从ABAP语言特性入手分析。 用户可能是ABAP开发者或学习者,正在研究内存管理相关的底层原理。这类问题通常出现在性能优化或排查内存泄漏的场景中,说明用户可能遇到了相关实际问题,或者在进行深入的技术学习。 从技术角度看,ABAP作为应用服务器语言确实有独特的内存模型。(stack)通常用于存储生命周期与函数调用绑定的局部变量,而(heap)用于动态分配的对象。但ABAP的特殊性在于它的会话内存(session memory)共享内存(shared memory)机制。 需要特别注意ABAP的三大内存区: 1. 内部会话的私有(PRIVATEMEMORY)对应调用层面 2. 用户会话的ABAP内存(EXPORT/IMPORT) 3. 应用服务器共享内存 对于局部变量,比如在FORM或METHOD内部声明的变量,虽然逻辑上类似分配,但物理上仍在内存中,只是受限于会话生命周期。而用CREATE OBJECT创建的实例则明确在上。 用户可能存在的认知误区是直接用C/C++的内存模型套用ABAP。需要强调ABAP虚拟机(SAP内核)的托管特性,开发者通常不需要直接操作内存地址。 另外注意到引用内容似乎与问题无关,可能是系统错误加载了其他资料。应该忽略这些不相关的引用,专注于ABAP技术本身的解释。 最后考虑补充变量作用域对存储位置的影响,比如模块池中的全局变量与子程序局部变量的差异。这能帮助用户建立更完整的认知框架。</think>在ABAP中,变量存储位置()由其**作用域声明方式**决定,但ABAP的内存管理机制与C/C++等底层语言存在显著差异。以下是关键点分析: ### 一、核心概念 1. **ABAP运行环境特点** ABAP程序在SAP应用服务器(ABAP VM)上运行,内存由**SAP内存管理系统**统一管理。开发者通常不直接操作物理内存地址,而是通过ABAP运行时环境(如工作进程)的抽象层进行内存分配。 2. **空间(Stack)** - 存储**局部变量****方法调用帧**。 - 生命周期与作用域绑定:当程序进入子程序(FORM, METHOD)时,局部变量上分配;退出子程序时自动释放。 - 示例: ```abap METHOD calculate_sum. DATA lv_result TYPE i. " 局部变量lv_result存储上 ... ENDMETHOD. ``` 3. **空间(Heap)** - 存储**动态创建的对象****跨作用域存活的数据**。 - 需要显式/隐式创建,通过**引用(Reference)** 访问。 - 关键类型: - **对象实例**:`CREATE OBJECT ref` 显式分配在上。 - **引用类型变量**:如`DATA ref TYPE REF TO class`,引用本身在,指向的对象在。 - **全局内存变量**:`EXPORT/IMPORT` 使用的ABAP内存(跨程序会话)存储中。 --- ### 二、存储位置规则 | 变量类型 | 存储位置 | 生命周期 | 示例 | |-----------------------|----------|------------------------|--------------------------| | 局部变量 (FORM/METHOD) | | 子程序执行期间 | `DATA lv_temp TYPE string` | | 对象实例 (OOP) | | 直到无引用指向时GC回收 | `CREATE OBJECT lo_obj` | | ABAP内存变量 | | 用户会话期间 | `EXPORT p_var TO MEMORY` | | 全局程序变量 | 混合管理 | 程序运行期间 | `PROGRAM. DATA gv_global` | > 注:全局变量(如在程序顶层声明的变量)由ABAP运行时动态管理,物理位置可能是,但对开发者透明。 --- ### 三、关键差异与注意事项 1. **无手动内存释放** ABAP采用**自动垃圾回收(Garbage Collection)** 管理内存,无需手动`DELETE`对象(对比C++的`delete`)。 2. **内存泄漏风险** 循环引用或全局容器持有对象引用会导致内存泄漏: ```abap DATA: gt_objects TYPE TABLE OF REF TO object. CREATE OBJECT lo_obj. APPEND lo_obj TO gt_objects. " 对象被全局表引用,无法GC回收 ``` 3. **共享内存扩展** SAP提供**跨会话共享内存**(如`SHARED MEMORY`/`SHARED BUFFER`),需特殊语法管理,常用于高性能场景[^1]。 --- ### 四、性能优化建议 - **优先**:局部变量访问速度快于对象。 - **避免过度全局化**:减少全局变量ABAP内存使用。 - **对象池技术**:重用对象减少分配开销。 - **内存分析工具**:使用`SAT`(运行时分析)或`SMEM`(内存监控)诊断瓶颈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值