WINDOWS堆栈区别[转]
堆栈的定义及应用

堆栈的定义及应用堆栈(Stack)是一种数据结构,它按照后进先出(LIFO)的原则存储数据。
也就是说,最后存入堆栈的数据元素最先被取出,而最先存入的数据元素最后被取出。
堆栈中包含两个主要操作:压栈(Push)和弹栈(Pop)。
压栈是指将数据元素存入堆栈,弹栈是指从堆栈中取出数据元素。
除此之外,还有一个查看栈顶元素的操作。
堆栈的实际应用非常广泛,以下列举几个常见的应用场景:1. 函数调用与递归:在程序中,每当一个函数被调用,系统将会为这个函数分配一段内存空间,这段内存空间就被称为函数的栈帧。
当函数执行完毕后,栈帧会被销毁。
函数调用过程中,每次调用都会将返回地址和相关参数等信息压入栈中,在函数执行完毕后再将这些信息弹出。
递归函数的实现也离不开堆栈,每次递归调用都会生成一个新的栈帧,直到递归结束后才开始回溯弹栈。
2. 表达式求值:在编程语言中,堆栈可以用于实现算术表达式求值。
例如,中缀表达式需要通过堆栈进行转换成后缀表达式来简化计算过程,然后再通过堆栈进行后缀表达式的计算。
在进行表达式求值时,通过堆栈可以保存运算符和操作数的顺序,确保运算的优先级正确。
3. 括号匹配:在编程或者数学等领域,括号匹配是一个常见的问题。
我们可以使用堆栈来判断一个表达式中的括号是否匹配。
遍历表达式,每当遇到左括号时,将其压入堆栈。
当遇到右括号时,从堆栈中弹出一个左括号,若左右括号匹配,则继续遍历。
若右括号没有对应的左括号或者堆栈为空,则括号不匹配。
4. 浏览器的历史记录:在浏览器中,通过点击链接或者前进后退按钮,我们可以在不同的网页之间进行切换。
这种网页切换也可以使用堆栈来实现浏览历史记录的功能。
每当访问一个新网页时,将其URL压入堆栈顶部;当点击前进按钮时,从堆栈中弹出一个URL;当点击后退按钮时,将当前页面的URL压入堆栈,然后再弹出上一个URL。
5. 撤销与恢复:在许多软件中,都提供了撤销与恢复功能。
当用户对文档进行操作时,软件会将操作信息(如添加、删除、修改等)压入堆栈中,当用户点击撤销时,软件会从堆栈中弹出最近的操作信息并进行撤销操作;当用户点击恢复时,软件会从堆栈中弹出已经撤销的操作信息并进行恢复。
内存中堆栈的划分

栈和堆的区别 (转) 终于知道区别了(2007-09-12 08:50:49)转载标签:IT/科技一个由 c/C++ 编译的程序占用的内存分为以下几个部分:1 、栈区( stack )—由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
2 、堆区( heap )—一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3 、全局区(静态区)( static )—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
程序结束后由有系统释放。
4 、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放。
5 、程序代码区—存放函数体的二进制代码。
例子程序:这是一个前辈写的,非常详细//main.cppint a = 0; //全局初始化区char *p1; //全局未初始化区main(){int b; 栈char s[] = "abc"; //栈char *p2; //栈char *p3 = "123456"; //123456在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区p1 = (char *)malloc(10);p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); //123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}栈:在 Windows 下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。
这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS 下,栈的大小是 2M (也有的说是 1M ,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示 overflow 。
.net中堆和栈的区别(图文解释)

尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。
同时,还需要具备一些基础的内存管理工作机制的知识,这样能够有助于解释我们日常程序编写中的变量的行为。
在本文中我将讲解栈和堆的基本知识,变量类型以及为什么一些变量能够按照它们自己的方式工作。
在.NET framework环境下,当我们的代码执行时,内存中尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。
同时,还需要具备一些基础的内存管理工作机制的知识,这样能够有助于解释我们日常程序编写中的变量的行为。
在本文中我将讲解栈和堆的基本知识,变量类型以及为什么一些变量能够按照它们自己的方式工作。
在.NET framework环境下,当我们的代码执行时,内存中有两个地方用来存储这些代码。
假如你不曾了解,那就让我来给你介绍栈(Stack)和堆(Heap)。
栈和堆都用来帮助我们运行代码的,它们驻留在机器内存中,且包含所有代码执行所需要的信息。
* 栈vs堆:有什么不同?栈负责保存我们的代码执行(或调用)路径,而堆则负责保存对象(或者说数据,接下来将谈到很多关于堆的问题)的路径。
可以将栈想象成一堆从顶向下堆叠的盒子。
当每调用一次方法时,我们将应用程序中所要发生的事情记录在栈顶的一个盒子中,而我们每次只能够使用栈顶的那个盒子。
当我们栈顶的盒子被使用完之后,或者说方法执行完毕之后,我们将抛开这个盒子然后继续使用栈顶上的新盒子。
堆的工作原理比较相似,但大多数时候堆用作保存信息而非保存执行路径,因此堆能够在任意时间被访问。
与栈相比堆没有任何访问限制,堆就像床上的旧衣服,我们并没有花时间去整理,那是因为可以随时找到一件我们需要的衣服,而栈就像储物柜里堆叠的鞋盒,我们只能从最顶层的盒子开始取,直到发现那只合适的。
堆栈知识详解(简单易懂)ppt课件

• 堆栈的应用
– 括号匹配、汉诺塔、火车车厢重排 – 迷宫、开关盒布线、离线等价类
信息技术科学学院
最新编辑ppt
7
公式化描述:继承线性表
template<class T>
线性表尾部作为栈顶
class Stack : private LinearList <T> {
// LIFO objects
到表尾
{Insert(Length(), x); return *this;}
Stack<T>& Pop(T& x)
{Delete(Length(), x);
return *this;} };
出栈——提取最后一个元素
信息技术科学学院
最新编辑ppt
9
实现方法分析
• IsFull需要获取数组大小
– 方法一 将类LinearList的成员MaxSize变为protected类型
第5章 堆栈
----后进先出:一种操作受限的线性表
信息技术科学学院
主要内容
• 堆栈的定义 • 堆栈的描述
– 公式化描述 – 链表描述
• 堆栈的应用
– 括号匹配、汉诺塔、火车车厢重排 – 迷宫、开关盒布线、离线等价类
信息技术科学学院
最新编辑ppt
2
堆栈定义
• 堆栈(stack)是一个线性表, 其插入和删除操作都在表的同一端进行,这 端被称为栈顶(top), 另一端被称为栈底(bottom)
• LIFO
push
E
Last in, F栈ir顶st out
top E
x E
栈顶
DD
栈与队列,各有异同。

栈与队列,各有异同。
⾸先是两者的定义:栈也称为堆栈,是⼀种线性表。
栈的特性:最先放⼊栈中的内容最后被拿出来,最后放⼊栈中的内容最先被拿出来,被称为先进后出、后进先出。
队列也是⼀种特殊的线性表。
不同于栈所服从的先进后出的原则,队列的原则是先进先出。
队列在队头做删除操作,在队尾做插⼊操作。
然后是两者的异同点不同点:1.删除数据元素的位置不同,栈的删除操作在表尾进⾏,队列的删除操作在表头进⾏。
2.队列先进先出,栈先进后出。
3.顺序栈能够实现多栈空间共享,⽽顺序队列不能。
4.遍历数据速度不同。
栈只能从头部取数据,也就最先放⼊的需要遍历整个栈最后才能取出来。
队列则不同,它基于地址指针进⾏遍历,⽽且可以从头或尾部开始遍历⽆需开辟临时空间,速度要快的多。
相同点:1.都是。
2.插⼊操作都是限定在表尾进⾏。
3.都可以通过顺序结构和链式结构实现。
4.插⼊与删除的时间复杂度与空间复杂度上两者均相同。
再然后便是两者的表⽰和操作的实现栈表⽰和操作的实现:#include <iostream>#define MAXSIZE 100//基础容量using namespace std;typedef struct{SElemType *top;//栈顶指针SElemType *base;//栈底指针int stacksize;//栈可⽤最⼤容量}SqStack;Status InitStack(SqStack &S)//初始化栈{S.base=new SElemType[MAXSIZE];if(!s.base) exit(OVERFLOW);//内存分配失败S.top=s.base;S.stacksize=MAXSIZE;}Status Push(SqStack &S,SElemType e)//把元素e压⼊栈顶{if(S.top-S.base==S.stacksize) return ERROR;//栈满*S.top++=e;//栈顶指针+1return OK;}Status Pop(SqStack &s,SElemType &e)//取出栈顶元素,并删除栈顶{if(S.top==S.base)//top与base重合时,栈为空return ERROR;e=*--S.top;return OK;}SElemType GetTop(SqStack S){if(S.top!=S.base)return *(S.top-1);}队列表⽰和操作的实现:#ifndef STATICQUEUE_H_INCLUDED#define STATICQUEUE_H_INCLUDEDtemplate<class T>class StaticQueue{public:StaticQueue();StaticQueue(int size);~StaticQueue();void enqueue(T data);T dequeue();bool isEmpty();bool isFull();int count();void display();private:int rear;int front;int size;const static int DEFAULT;T* queue;};这些在课本上都有,下⾯说说遇到的问题:对于作业3,可以说是屡战屡败,屡败屡战了,先是⼀点思路都没有,再到后来⽼师提⽰后有⼀点思路,但还是错误百出,再到后来参照书上的⽅法,还是错误,最后终于发现问题。
堆栈技术的原理和应用

堆栈技术的原理和应用什么是堆栈技术堆栈(Stack)是一种基于后入先出(Last-In-First-Out,LIFO)的数据结构,它可以用来存储和管理数据。
堆栈技术在计算机科学领域被广泛应用,包括操作系统、编程语言和网络等方面。
堆栈技术的原理在堆栈技术中,数据是按照先进后出的顺序被存储和检索的。
堆栈有两个基本操作:入栈(Push)和出栈(Pop)。
•入栈(Push)操作将数据放入堆栈的顶部,也就是最后一个元素的上方。
此时,数据成为新的堆栈顶部。
•出栈(Pop)操作将堆栈顶部的数据移除,并返回该数据。
此时,堆栈顶部被更新为上一个元素。
堆栈操作可以用指针或索引来实现。
当指针指向堆栈的顶部时,可以通过修改指针的位置来执行入栈和出栈操作。
堆栈技术的应用堆栈技术在计算机科学中有多种应用,下面列举了几个常见的应用场景。
1.函数调用:堆栈被用于保存函数调用的上下文信息。
每当一个函数被调用,相关的参数和返回地址等信息都会被压入堆栈。
当函数调用结束后,这些信息会被弹出堆栈,返回到调用点。
2.表达式求值:堆栈可以用于求解数学表达式,包括中缀表达式和后缀表达式。
在中缀表达式求值过程中,运算符和操作数会被依次压入堆栈,直到出现优先级更高的运算符或遇到右括号。
而在后缀表达式求值过程中,每当遇到一个操作数,都可以通过堆栈来存储和管理。
3.内存管理:堆栈技术在内存管理中起到重要的作用。
每当一个函数被调用,其本地变量、临时变量和返回值等数据会被存储在堆栈中。
这样可以方便地分配和释放内存空间,同时确保函数调用的独立性。
4.操作系统:堆栈技术在操作系统中被广泛应用,用于管理程序的执行和系统资源的调度。
操作系统会使用堆栈来维护进程的执行状态,包括程序计数器、寄存器和其他上下文信息。
5.编程语言:许多编程语言都支持堆栈数据结构,例如C语言中的函数调用堆栈、Java语言中的方法调用堆栈和Python语言中的运行时堆栈。
这些堆栈可以用于管理函数调用、异常处理和递归等操作。
队列,栈,堆栈,数组,链表特点与区别

队列,栈,堆栈,数组,链表特点与区别1. 队列可以看成是有2个口的集合一个口叫队头一个叫队尾,只能在对头进行删除操作,在队尾做插入。
根据这样的操作。
队列特点是先进先出2.堆栈可以看成是有1个口的集合,这个口叫栈顶。
插入和删除操作只能在栈顶操作。
根据这样的操作。
堆栈的特点是是后进先出.3.链表是一种存储方式,它可以在非连续的内存空间里面存储一个集合的元素。
4.和它对应的是数组,数组要在连续的空间里存储集合的元素队列、栈是线性数据结构的典型代表,而数组、链表是常用的两种数据存储结构;队列和栈均可以用数组或链表的存储方式实现它的功能数组与链表:数组属于顺序存储中,由于每个元素的存储位置都可以通过简单计算得到,所以访问元素的时间都相同(直接访问数组下标);链表属于数据的链接存储,由于每个元素的存储位置是保存在它的前驱或后继结点中的,所以只有当访问到其前驱结点或后继结点后才能够按指针访问到自己,访问任一元素的时间与该元素结点在链接存储中的位置有关。
链表和数组是常用的两种数据存储结构,都能用来保存特定类型的数据。
1.占用的内存空间链表存放的内存空间可以是连续的,也可以是不连续的,数组则是连续的一段内存空间。
一般情况下存放相同多的数据数组占用较小的内存,而链表还需要存放其前驱和后继的空间。
2.长度的可变性链表的长度是按实际需要可以伸缩的,而数组的长度是在定义时要给定的,如果存放的数据个数超过了数组的初始大小,则会出现溢出现象。
3.对数据的访问链表方便数据的移动而访问数据比较麻烦;数组访问数据很快捷而移动数据比较麻烦。
链表和数组的差异决定了它们的不同使用场景,如果需要很多对数据的访问,则适合使用数组;如果需要对数据进行很多移位操作,则设和使用链表。
堆和栈有什么区别:1. 栈具有数据结构中栈的特点,后进先出,所有存放在它里面的数据都是生命周期很明确(当然要求它不能存放太久,占有的空间确定而且占用空间小),能够快速反应的!所有在Java中它存放的是8个基本数据类型和引用变量的,用完就马上销毁2.堆可以理解它就是个一个可大可小,任你分配的听话的内存操作单元;因此它的特点就是动态的分配内存,适合存放大的数据量!比如一个对象的所有信息,虽然它的引用指向栈中的某个引用变量;所有Java中堆是存放new 出来的对象的。
堆栈型机器名词解释

堆栈型机器名词解释1. 引言在计算机科学领域中,堆栈(stack)是一种数据结构,它采用了“后进先出”(Last In, First Out,LIFO)的原则。
堆栈型机器是一种基于堆栈数据结构实现的计算机系统,其中计算机指令和数据都存储在堆栈中。
本篇文章将为您解释堆栈型机器的概念,包括其原理、应用和优缺点。
2. 堆栈型机器的原理堆栈型机器的基本原理是使用堆栈来处理数据和指令。
堆栈由两个主要操作组成,即入栈(push)和出栈(pop)。
当一个元素被入栈时,它被添加到堆栈的顶部;而当一个元素被出栈时,它从堆栈的顶部被移除。
在堆栈型机器中,计算机指令和操作数被存储在堆栈中。
当需要执行某个指令时,操作数从堆栈中弹出,执行相应的运算,并将结果重新压入堆栈。
这种设计思路简化了计算机的指令集,使得堆栈型机器在某些应用场景下具有较高的效率。
3. 堆栈型机器的应用堆栈型机器在许多领域都有广泛的应用。
其中一个典型的应用场景是编程语言的解释器和虚拟机。
Java虚拟机就是一种堆栈型机器,它执行Java字节码指令。
由于Java字节码是基于堆栈的,因此堆栈型机器非常适合执行这种类型的指令。
堆栈型机器还被广泛应用于图形计算、嵌入式系统和计算机网络等领域。
在这些领域中,堆栈型机器可以提供高效的计算和优化的内存管理,从而快速响应需求并节省资源。
4. 堆栈型机器的优缺点堆栈型机器具有一些显著的优点,也有一些局限性。
由于堆栈型机器的指令集相对较小,编程语言实现和编译器的开发相对容易。
堆栈型机器具有良好的可移植性,因为它们不依赖于具体的硬件架构。
由于堆栈型机器使用的是后进先出的执行顺序,它可以轻松地处理递归调用和函数嵌套。
然而,堆栈型机器也存在一些局限性。
由于数据和指令都存储在堆栈中,堆栈的大小限制了堆栈型机器的存储能力。
由于堆栈型机器的指令执行顺序是固定的,它在某些情况下可能会受到性能影响。
5. 总结与展望在本篇文章中,我们对堆栈型机器进行了深入解析。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
WINDOWS堆栈区别[转]堆和栈的区别(转贴)非本人作也!因非常经典,所以收归旗下,与众人阅之!原作者不祥!堆和栈的区别一、预备知识—程序的内存分配一个由C/C++编译的程序占用的内存分为以下几个部分1、栈区(STACK)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
2、堆区(HEAP)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(STATIC)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
- 程序结束后有系统释放4、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放5、程序代码区—存放函数体的二进制代码。
二、例子程序这是一个前辈写的,非常详细//MAIN.CPPINT 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是在编译时就确定的;但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。
比如:#INCLUDEVOID 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],CL11: 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小结:堆和栈的区别可以用如下的比喻来看出:使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
WINDOWS进程中的内存结构在阅读本文之前,如果你连堆栈是什么多不知道的话,请先阅读文章后面的基础知识。
接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据。
那么这些变量在内存中是如何存放的呢?程序又是如何使用这些变量的呢?下面就会对此进行深入的讨论。
下文中的C语言代码如没有特别声明,默认都使用VC编译的RELEASE版。
首先,来了解一下 C 语言的变量是如何在内存分部的。
C 语言有全局变量(GLOBAL)、本地变量(LOCAL),静态变量(STATIC)、寄存器变量(REGEISTER)。
每种变量都有不同的分配方式。
先来看下面这段代码:#INCLUDE <STDIO.H>INT G1=0, G2=0, G3=0;INT MAIN(){STATIC INT S1=0, S2=0, S3=0;INT V1=0, V2=0, V3=0;//打印出各个变量的内存地址PRINTF("0X%08X\N",&V1); //打印各本地变量的内存地址PRINTF("0X%08X\N",&V2);PRINTF("0X%08X\N\N",&V3);PRINTF("0X%08X\N",&G1); //打印各全局变量的内存地址PRINTF("0X%08X\N",&G2);PRINTF("0X%08X\N\N",&G3);PRINTF("0X%08X\N",&S1); //打印各静态变量的内存地址PRINTF("0X%08X\N",&S2);PRINTF("0X%08X\N\N",&S3);RETURN 0;}编译后的执行结果是:0X0012FF780X0012FF7C0X0012FF800X004068D00X004068D40X004068D80X004068DC0X004068E00X004068E4输出的结果就是变量的内存地址。
其中V1,V2,V3是本地变量,G1,G2,G3是全局变量,S1,S2,S3是静态变量。
你可以看到这些变量在内存是连续分布的,但是本地变量和全局变量分配的内存地址差了十万八千里,而全局变量和静态变量分配的内存是连续的。
这是因为本地变量和全局/静态变量是分配在不同类型的内存区域中的结果。
对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。
动态数据区一般就是“堆栈”。
“栈(STACK)”和“堆(HEAP)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。
进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。
一个堆栈可以通过“基地址”和“栈顶”地址来描述。
全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。
程序通过堆栈的基地址和偏移量来访问本地变量。
├———————┤低端内存区域│……│├———————┤│动态数据区│├———————┤│……│├———————┤│代码区│├———————┤│静态数据区│├———————┤│……│├———————┤高端内存区域堆栈是一个先进后出的数据结构,栈顶地址总是小于等于栈的基地址。
我们可以先了解一下函数调用的过程,以便对堆栈在程序中的作用有更深入的了解。
不同的语言有不同的函数调用规定,这些因素有参数的压入规则和堆栈的平衡。
WINDOWS API的调用规则和ANSI C的函数调用规则是不一样的,前者由被调函数调整堆栈,后者由调用者调整堆栈。
两者通过“__STDCALL”和“__CDECL”前缀区分。
先看下面这段代码:#INCLUDE <STDIO.H>VOID __STDCALL FUNC(INT PARAM1,INT PARAM2,INT PARAM3){INT VAR1=PARAM1;INT VAR2=PARAM2;INT VAR3=PARAM3;PRINTF("0X%08X\N",¶M1); //打印出各个变量的内存地址PRINTF("0X%08X\N",¶M2);PRINTF("0X%08X\N\N",¶M3);PRINTF("0X%08X\N",&VAR1);PRINTF("0X%08X\N",&VAR2);PRINTF("0X%08X\N\N",&VAR3);RETURN;}INT MAIN(){FUNC(1,2,3);RETURN 0;}编译后的执行结果是:0X0012FF780X0012FF7C0X0012FF800X0012FF680X0012FF6C0X0012FF70├———————┤<—函数执行时的栈顶(ESP)、低端内存区域│……│├———————┤│VAR 1 │├———————┤│VAR 2 │├———————┤│VAR 3 │├———————┤│RET │├———————┤<—“__CDECL”函数返回后的栈顶(ESP)│PARAMETER 1 │├———————┤│PARAMETER 2 │├———————┤│PARAMETER 3 │├———————┤<—“__STDCALL”函数返回后的栈顶(ESP)│……│├———————┤<—栈底(基地址EBP)、高端内存区域上图就是函数调用过程中堆栈的样子了。