关于栈,我们并不陌生,从学习C语言其就接触了,数据结构中有学过,其提供的操作很简单,主要的操作就push和pop。一般来说在需要暂存数据的时候我们一般使用栈来临时存储数据,这也解开了学习C语言以来的另一个困惑:函数的调用约定,C语言中我们经常碰到的调用约定是__stdcall,__cdecl。而__thiscall,__fastcall碰到的很少,简单说下,就是在函数调用时,当参数多余1个时,参数是按照什么顺序入栈的(注意这里是使用栈来存储数据的)和在函数调用后,由谁(调用者还是被调用者)把参数弹出栈。__stdcall的调用约定,参数从右向左压入栈和函数自身把参数弹出栈。__cdecl的调用约定,参数采用从右到左的压栈和每一个调用它的函数都包含清空堆栈的代码。__thiscall仅仅应用于成员函数,this指针存放于CX寄存器,参数从右到左压。__fastcall调用约定,它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送和被调用的函数在返回前把参数弹出栈)。
关于更详细的调用约定可以参考http://blog.csdn.net/fly2k5/article/details/544112和http://www.cppblog.com/mzty/archive/2007/04/20/22349.html这两篇博客看看。
在汇编中也提供了栈的处理机制,在CPU中有两个寄存器,段寄存器SS(Stack Segment)和寄存器SP(Stack Pointer)。栈顶的段地址放在SS中,偏移地址存放在SP中,通过改变SP的值实现push和pop操作。内存地址可以用SS:SP来定位。push操作的执行分为两步(如:push 23):(1)SP=SP-2【可以考虑下为什么是-2而不是-1?指明了栈操作是按照字来操作的,若是-1的话,说明栈操作是按照字节来操作的(实际上是不存在的)】,使SS:SP指向栈顶前面的单元,以当前栈顶前面的单元为新的栈顶。(2)将23送入SS:SP指向的内存单元,此时SS:SP指向了新的栈顶。同样,在处理二维数组的时候也用到了栈,前面说过,栈可以用来临时存放数据。在CPU中默认寄存器ECX为循环计数器,而在处理二维数组的时候需要两个循环计数器,也许有人会想到用另外一个寄存器来处理,就目前的问题的确是解决了,但是当我们遇到更复杂的问题(需要临时存放多个数据或多,层循环怎么办),毕竟CPU中提供的寄存器有限。此时我们要寻求一种一般的解决方案,那就是用栈来解决,在内存中开辟一段内存用作栈使用来临时存放数据,这样就可以根据需要在内存中开辟多大的内存当作栈使用来存放数据。看个例子,将下面每个单词(display,brow,replace,modify)的前四个字符改为大写。
assume cs:codesg,ds:datasg,ss:stacksg;
stacksg segment
dw 0,0,0,0,0,0,0,0 ;
stacksg ends
datasg segment
db '1.display.......'
db '2.brow..........'
db '3.replace.......'
db '4.modify........'
datasg ends
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,16 ;将stacksg与SS链接(定义栈,并初始化栈空,使SP指向栈中最后一个元素的下一个单元地址)
mov ax,datasg
mov ds,ax
mov bx,0 ;使DS与datasg链接,要处理的数据,定义行,bx定位行
mov cx,4 ;cx为循环计数器
s0:push cx ;入栈,覆盖栈中第一个数据
mov si,0 ;定义列
mov cx,4
s:mov ax,[bx+3+si] ;定位到每个要索引的字母,总共每行有四个字母,si定位列,将内存地址[bx+3+si]中字符放到ax中,相当于高级语言中的赋值
and ax,11011111b ;实现使寄存器ax(ax为16位的,分为ah,al)中的字符变为大写
mov [bx+3+si],ax ;将ax中变为大写的字符写回内存中
inc si ;列变量si自增1,使si指向写一个字符
loop s ;loop指令默认cx为循环计数器
add bx,16 ;使bx指向下一行
pop cx ;出栈,将栈中的数据(cx=4)放到cx中,即恢复cx的值4,以便进行外层循环
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
这里便再次体现了栈的重大作用。
在汇编中可以使用[bx].idata[si]([bx].idata[si]=[bx+idata+si])定位内存单元(寻址),先定义一个结构体,如下,
struct company
{
char companyName[3];
char founder[9];
int ranking;
char productor[3];
};
struct companproductory dec={"DEC","Ken Olsen",70,"PSP"};
此时dec.[i],dec是一个结构体变量名,指明了结构体变量的地址,productor是结构体中的数据项,指明了数据项productor的地址,而i用来指定productor中的每一个字符,对应的汇编语言中的源码为bx.10h[si]。10h为productor的地址。