3.6 栈
- 栈是一种具有特殊的访问方式的存储空间,他的特殊性就在于 最后一个进入这个空间的数据,是最先出去的
栈有两种基本的操作:入栈和出栈
- 入栈:将一个新的元素放到栈顶
- 出栈:从栈顶取出一个元素
- 栈顶元素总是最后一个入栈的,需要出的时候,又会是第一个被取出的
操作规则:LIFO
- (Last in first out) 后进先出
3.7 CPU提供的栈机制
- 现今的CPU都有栈的设计, CPU提供相关的指令来以栈的方式访问内存空间
这就意味着,我们再CPU编程的时候,可将一段内存当做栈来使用
push(入栈):
- push ax: 将寄存器ax中的数据送入栈中
pop(出栈):
- pop ax:从栈顶取出数据送入ax中
入栈和出栈的操作都是以 字 为单位进行的
- 字型数据用两个单元存放,高地址单元放高8位,低地址单元房地8位

1. CPU如何知道一段内存空间被当做栈使用?
有两个寄存器:
- 段寄存器:SS 存放栈顶的段地址
- 寄存器:SP 存放栈顶的偏移地址
- 任意时刻 SS:SP指向栈顶元素
2. 执行push和pop的时候,如何知道那个单元是栈顶单元的?

- push ax
- 先进行:SP = SP - 2
- 然后将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新的栈顶

- pop ax
- 先将栈顶(SS:SP)数据拿出来给ax
- 然后:SP = SP + 2
知识点:
- 当pop之后其实栈中的数据还是存在的但是并不属于栈中内内容了、不能操作、当下一次push的时候回将其进行覆盖,并没有一个真正的删除,和硬盘格式化之后还可以恢复是一个道理,只是改变的指针的指向
如果将10000~1000F作为栈的空间:初始转态的栈是空的,此时:
- ss=1000
- sp=0010(F的高一位地址 )
- 栈空间大小为16个字节,栈最底部的字节单元地址为 1000:000E
任意时刻 SS:SP指向栈顶,当栈中只有一个元素的时候:
**SS=1000,SP=000E**- 栈为空,就相当于栈中唯一的元素被pop,出栈后,SP=SP+2,SP原来为000E,加2后SP=10
- 所以当栈为空的时候,SS=1000,SP=10
换个角度说:
- 任意时刻,SS:SP指向栈顶元素,当栈为空时候,栈中没有元素,也就不存在栈顶元素
- 所以SS:SP只能指向栈的最底部单元下面的单元,该单元中的偏移地址为栈最底部的字节单元的偏移地址+2
- 栈最底部字单元的地址为1000:000E,所以栈为空时 SP=000E-2=0010
只有栈内有元素的时候才有栈顶,否则不存在栈顶
3. CPU如何知道当前要执行的指令所在的位置?
- 寄存器cs和ip中存放着当前执行的段地址和偏移地址
3.8 栈顶超界的问题

- SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈是找到栈顶
- 当栈满的时候再使用push指令入栈,栈空的时候在pop指令出栈都会发证栈顶超界的问题。 栈顶越界是危险的,因为会覆盖掉其他数据
- 因为我们既然将一段空间安排为栈,那么在栈空间之外的空间里很可能存放了一些具有其他用途的数据、代码等、这些数据、代码可能使我们自己程序中的、也有可能是别的程序中的、如果是系统关键内容就危险了
- 但是由于我们在入栈时的不小心而将这些数据、代码意外的改写、都会引发一连串的错误。我么当然希望CPU可以帮我们解决这个问题
可是,如何保证在入栈,出栈时,栈顶不会超出栈空间?
其实CPU在执行的时候只考虑两种情况
- 当前栈顶在何处
- 当前执行的指令是那一条
结论:
- 我们再编程的时候要看自己操心栈的超界问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界了;
- 执行出栈操作的时候也要注意,以防栈空的时候继续出栈而导致超界
3.9 push、pop指令
push指令和pop指令是可以在寄存器和内存之间传送数据的
栈与内存:
- 栈空间当然也是内存空间的一部分,它是一段可以以一种特殊方式进行访问的内存空间
- 栈只是一种数据结构的存放格式
push和pop的指令格式(1)
- push 寄存器:将一个寄存器的数据入栈
- pop 寄存器: 出栈,用一个寄存器接受出栈的数据
如:
- push ax
- pop bx
push和pop的指令格式(2)
- push 段寄存器:将一个段寄存器的数据入栈
- pop 段寄存器:出栈, 用一个段寄存器接受出栈的数据
如:
- push ds
- pop es
push和pop的指令格式(3)
- push 内存单元:将一对内存单元的数据入栈(栈操作都是以字为单位的)
- pop 内存单元:出栈, 用一对内存单元接受出栈的字单元数据
如:
- push [0]
- pop [2]
执行指令时,CPU要知道内存单元的地址,可以在push,pop指令中给出内存单元的偏移地址,段地址是在指令执行时,CPU从ds中自动获取的
段寄存器都是以S结尾的,通用寄存器都是以X结尾的
- 段地址在执行时可以从DS获得
- 数据的段地址永远是从DS获得
- 代码的段地址永远是从CS中获得
- 栈的段地址永远是从SS中获得

# 设置栈的段地址,ss=1000,不能直接向段寄存器送入数据,要通过ax通用寄存器传递mov ax, 1000mov ss, ax# 设置栈顶的偏移地址,因为栈为空,所以sp=0010mov sp, 0010# 压入数据push axpush bxpush ds
mov ax, 1000mov ss, axmov sp, 0100mov ax=001amov bx=001bpush axpush bxsub ax, axsub bx, bxpop bxpop ax- 从上面的程序可以看到,用栈来暂存以后需要恢复的寄存器中的内存时,出栈的顺序要和入栈的顺序相反, 因为最后入栈的寄存器的内容在栈顶,所以在恢复时,是最先出栈的

mov bx, 1000mov ss, bxmov ap, 10mov ax, 002amov bx, 002bpush axpush bxpop axpop bx
mov ax, 1000mov ss, axmov sp, 2mov ax, 226push, ax- Pop、和push实际上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是 push和pop指令访问的内存单元的地址不是在指令中给出,而是ss:p指出的
- 我们需要是十分清楚的是,push和pop指令同mov指令不同, CPU执行mov指令只需要一步, 而执行push、pop指令却需要两步操作
执行push时:
- 先改变sp,后向ss:sp压入
执行pop时:
- 先读取ss:sp处的数据, 后改变sp
- push、pop指令修改的只是sp,也就是说栈顶的变化范围最大为:0~FFFF(SP寄存器的大小)
- 在ss:sp中存放栈顶的段地址和偏移地址, 提供入栈和出栈指令, 他们根据ss:sp指示的地址, 按照栈的方式访问内存单元
- 任意时刻。ss:sp执行栈顶元素
- 8086CPU只记录栈顶, 栈空间的大小我们要自己管理
用栈来暂存以后需要恢复的寄存器的内容, 寄存器出栈的顺序要和 入栈相反
- 栈是一种非常重要的机制, 一定要深入理解, 灵活掌握
继续阅读与本文标签相同的文章
-
数十万共享雨伞不翼而飞,创始人却高兴的要命!网友:赚翻了
2026-05-18栏目: 教程
-
滴滴 这是一见钟情的感脚
2026-05-18栏目: 教程
-
以实践的方式讨论:N-Gram原理与其应用
2026-05-18栏目: 教程
-
Hi拼团,第六代云服务器拼团购买更便宜,低至148元/年
2026-05-18栏目: 教程
-
汇编(五)栈、CPU提供的栈机制、push、pop指令
2026-05-18栏目: 教程
