首先,栈是一个只有sp一端开口的长管子(其实就是把常规说法的栈只能在一段插入或取出数据的另一种讲法)

8086的汇编层面对栈及pushpop的初步理解_汇编

ss寄存器(stack segment)指向栈底即无法写入数据的一端,sp寄存器(stack pointer)指向栈顶,即用于取出和存放数据的一端。

ss在这里是基地址,而sp是偏移地址

所以在下面的代码中ss:sp指向的物理地址位(2000h*16+30h)=20030h

在汇编语言中用法如下

mov ax,2000h
mov ss,ax
mov sp,30h

在汇编中经常出现某些寄存器无法直接读入数据,而只能通过寄存器间接传递,如ss。sp则不需要。实话说我也记不住所有寄存器哪些可以直接修改,毕竟我是小白哈哈。

刚接触push和栈的时候我有一些疑惑,push和pop为什么是单元操作符。

后来发现其实这里的(ss:sp)是被隐式了,像是下面的代码

mov ax,2000h
mov ss,ax
mov sp,30h
push ax

其实是把ax的值储存在ss:sp指向的地址单元和下一个地址单元

因为8086汇编中一个栈单元储存一个字大小的数据,即两个字节

在这之后还发生了一件,sp的值需要改变,否则下一次push的时候传入的值还是在原来的ss:sp,这肯定是不合理的,原来的数据会被覆盖

那么sp指向的位置应该怎么变换呢,首先可以根据刚才说的(一个栈单元储存两个字节大小的数据)确定是+2或者-2,

其次sp指向的位置其实应该向栈底靠拢,否则这个栈的空间岂不是无穷大了,这里ss的作用才真正体现出来,标记应该在哪里停下(实际上这点ss也没做好,因为汇编是一门很基础的语言,他没有限制你的sp在到达ss之后不能继续写入,这里的漏洞就是栈溢出漏洞)

所以这里确定sp在完成入栈操作后需要+2

还有很重要的一点,ss指向的栈底是高地址,而sp指向的栈顶是低地址

综合上述红色字体

一次push实际完成的操作如下

mov ss:[sp],ax	;把ax的值传入ss:[sp]
add sp,2				;栈顶指针指示器(sp)加2,向ss栈底靠拢

这里的操作就等价于

push ax

所以可以看到push指令其实就是语法糖,简化过程。

push和pop是一模一样的操作

mov ax,ss:[sp]	;将ss:[sp]的值传入ax	
sub sp,2				;sp减2,释放了一个栈空间

最后在提一下所谓的后进先出(last in first out)LIFO

其实这一性质是由栈只能一端进出决定的

假设一个栈里面你先放了1111,再放2222,最后放3333

那么你要想拿到1111的数据,就必须先取出3333,再取出2222,最后才能拿到1111

这一性质可以帮助我们在没有中间变量的情况交换变量的值

push ax
push bx
pop ax
pop bx

这里其实可以体现汇编语言就像一把手术刀一样,用的好就可以直取要害

假设在c语言要达到这样的操作是不可能的,因为c没有办法直接对栈进行操作。当然也可以用异或等操作达到快速交换的效果,但是我不会这就不属于汇编的范畴了,先不讨论。