对 栈 的理解
一,初始
栈是一种具有特殊访问方式的储存空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。
- 可以用一个盒子和3本书描述

可以想象一下,如果这三本书是一行行代码,那么,它的解释入栈和出栈原理就是和图中所示的大概一样。
二、栈的操作
- 入栈:将一个新的元素放到栈顶
- 出栈:从栈顶取出一个元素
栈顶的元素总是最后入栈,需要出栈时,又总是最先被从栈中取出
这种操作规则:LIFO(Last in First out,后进先出)
一般来说,在汇编中,入栈和出栈的指令是最基本的,push(入栈),pop(出栈)
如:
push ax将寄存器ax中的数据送入栈中
pop ax从栈顶取出数据放到ax寄存器中
8086CPU的入栈和出栈方式都是以字(16位)为单位进行的
三、举例理解
如果将10000H~1000FH这段内存当做栈来使用,理解以下指令
1 | mov ax,0123H |
分解指令理解
初始栈的模型

mov ax,0123H和push ax将
0123H放入ax中,然后将ax中的数据push 进栈
同理,
mov bx,2266H和push bx将
2266H放入bx寄存器中,再将bx中的数据push进栈
同理,
mov cx,1122H和push cx
pop axpop指令是将栈顶的元素取出,放入ax中,由于这时栈顶的元素数据是1122H,所以将其取出
pop bx同理,这时pop取出栈顶的元素
2266H,并放入bx中
pop cx这时取出
0123H,将其放入cx中
由此,一个程序执行完毕,栈就恢复了原始状态
注意:字形数据用两个单元存放,高地址放高8位,低地址放低8位
四、CPU如何知道指令位置
通过了一个例子的执行,可能有个问题,CPU是如何知道要执行指令的位置
答案是,寄存器CS和IP中存放着当前指令的段地址和偏移地址,并且,在任意时刻,SS:SP都指向栈顶的元素。
push ax事实上执行了两步操作,
SP=SP-2将ax中的内容送入
SS:SP指向的内存单元处,SS:SP指向新的栈顶

pop深刻理解
pop与push是逆运算,pop ax同样也需要执行两步操作
- 将
SS:SP指向的单元中的数据取出,放到ax中 SP=SP+2,这时SS:SP指向了新的单元

由上面两步可以看出,所谓 ’取出‘ 数据,其实,并只取出了数据,并没有删除内存中的数据,而是将SS:IP的指向改变了,原来存入的数据还在,若想要删除,只能用新的数据覆盖
五、栈的大小
因为SS:SP只记录了栈顶的地址,依靠SS和SP可以保证入栈和出栈时找到栈顶,如何保证在入栈和出栈时,栈顶不会超出栈空间?
即是
- 当栈满的时候再使用push指令入栈
- 当栈空的时候再使用pop指令出栈
这就会导致栈顶超界问题,非常危险
栈顶超界

栈底超界

以上演示图片说明,栈顶超界是危险的
因为我们既然将一段空间安排为栈,那么在栈空间之外的空间里很可能存放了具有其它用途的数据、代码,可能是我们自己程序中的,也可能是别的程序中的,毕竟在计算机中并不是只有我们自己的程序在运行。
这就是所谓的栈溢出,从黑客角度,就是溢出攻击
六、设置栈
如果将10000H~1000FH这段空间当做栈,初始状态是空的,如何将AX,BX,DS中的数据入栈?
1 | mov ax,1000H |
栈的意义就在于暂时储存以后需要恢复的寄存器内容
七、参考链接
小甲鱼汇编入门:
https://www.bilibili.com/video/BV1zW411n79C
阮一峰-汇编入门教程:
https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html