广东省口腔医院 好么:堆栈指令是什么意思

来源:百度文库 编辑:高校问答 时间:2024/04/30 02:16:01

堆栈其实不只是我们平常意义上所谓的具有后进先出特性的数据结构。严格来讲并不存在堆栈这样一种结构,只是在日常工作中我们将前述的这种数据结构称为堆栈罢了,但其实确切的说应该叫做栈 (Stack) 。而堆 (Heap) 其实是另一种允许随意访问的数据存储空间。

首先从汇编的角度来理解堆和栈
我们都知道在汇编语言中有著名的三个段:代码段,数据段和堆栈段。同为存储数据为什么有两个不同区域呢?其实我们仔细想一下就会明白,我们所谓的堆栈段,或者说栈段,正是那种支持后进先出特性的内存区域。汇编语言里面的 POP 和 PUSH 两个指令就是来操作堆栈段的。而对于数据段我们可以在其中开辟自己命名的内存空间,然后使用指针来访问,这正是堆。

标准 C++
再将语言提升一个层次,在标准 C++ 中——这也许是我们在学校中接触最久的一个语言了,但有谁能说在学校里面学明白了呢?误人子弟呀!——是否也有这样的区别呢?答案是肯定的。先来看这样的一个类。

class MyClass {
public:
MyClass(int _a, int _b) {
a = _a;
b = _b;
}

~MyClass() {};

private:
int a;
int b;
};

然后我们声明 MyClass 类的实例,问题就出来了。

void main() {
MyClass myObj(1, 2); //此时该对象位于栈上
MyClass* pMyObj = new MyClass(1, 2); //此时该对象位于堆上,并通过指针与我们交流。
...
}

上面的示例似乎在表明这样一个原则,对象所存在的位置与程序员声明的方式有关。是的!不仅如此,栈对象和堆对象的行为也是不一样的。我们都知道从汇编角度来看,当一个子程序退出时,我们需要使用 RET n 来退栈,即将在子程序中使用过的内存空间释放。因此栈对象会随着方法执行的结束而自动释放,不会产生泄漏。而堆对象却是不可以的,因此我们才须要在方法退出之前,手动释放内存空间,即 delete pMyObj; 这也是我上面为什么给出省略号的原因。

在 .net Framework 中
在 .net 中问题又有所不同,由于内存被 CLR 托管,我们不能再随意地将对象放在你希望的位置上了。这部分工作完全由 CLR 来接管。CLR 的实现是所有的值对象都被放在栈上,当方法退出时自动销毁;而所有的引用对象都被放在托管堆上 (由 CLR 的内存回收服务控制的内存区域被称为托管内存或托管堆,而前面提到的标准 C++ 中的堆相应的被称为本地堆),通过托管的指针,在 C# 中是对象引用,在 C++/CLI 中是追踪句柄,来访问。它的释放不依赖于方法的退出,也不依赖于程序员,而是依赖于内存回收机制。
这里就引出了另外一个问题,为什么装箱 (Boxing) 和拆箱 (Unboxing) 操作会有性能损失。因为对于所有值类型都为栈上,而将其转变为引用类型 object 会发生两个动作,一是将值对象从栈拷贝到托管堆上,然后再给其加上一些原数据 (Metadata)使之可以被托管堆控制,这比无论是直接访问值对象还是引用对象而言都要额外消耗不少的时间,因此也就产生了性能问题。拆箱的原理刚好相反。

堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度低。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

push压栈
pusha所有16位寄存器进栈
pushad所有32位寄存器进栈
pushf标志位进栈

pop出栈
popa所有16位寄存器出栈
popad所有32位寄存器出栈
popf标志位出栈

push是压栈指令
pop 是出栈指令