编译器:vc++6.0(因为此种实现依赖编译器处理) 此处只简要叙述一下机制。并附部分关键指令序列。
准备: 1,关于EBP:称做栈基址指针。为什么这样说呢?我们先来看看函数调用的过程: 参数从右到左压栈。 call指令执行,该指令将导致EIP压栈。 每个函数前两句必定是:push ebp mov ebp,esp。则call指令后,跳到被调函数出开始执行。保存ebp,即ebp压栈。 局部变量压栈。一般是sub esp,xxx的形式。
这就是将ebp称为基指针的原因:ebp-xx访问的时局部变量;ebp+xx访问的是参数。最左边参数地址是ebp+8h。ebp坐镇中间为基准。
2,函数返回值统一放入eax中。只要放得下。 3,栈扩展方向为从高地址到低地址。结构内的变量存贮方式:低地址对应声明顺序靠前的成员。一定要注意这里的区别!它关系到反汇编生成的代码里面的数字是怎么算出来的。但如果你自己写汇编代码就不用考虑这些了。只取成员名即可。 4,不能直接在两个存储器变量间用mov指令。
主要原理: 当调用一个返回结构体的函数时,在vc++下,是这样处理: 首先sub esp,xx,在堆栈上开辟一个空间。大小为结构体大小。 然后lea eax,[esp-xx],即将结构体在堆栈中的地址送eax。 push 参数。 push eax。 call 函数。 被调用函数内部: routine: push ebp mov ebp,esp
一般是定义一个结构体局部变量: struct aa a; sub esp, sizeof aa
然后处理结构体, . . . . 最后return a。 首先mov eax,[ebp+8h] ;将外面的调用函数在堆栈内开辟的结构体指针赋予eax。 然后将被调用函数在堆栈内开辟的结构体内的值赋到调用函数开辟的结构体内。 一般形式是:mov ecx,[ebp-结构体大小] ;赋第一个成员 mov [eax],ecx mov ecx,[ebp-结构体大小+第一个成员大小(考虑对齐)] mov [eax+第一个成员大小],ecx ...... 赋完值后返回: add esp,sizeof aa mov esp,ebp pop ebp ret 此时被调用函数在堆栈内开辟的结构体空间被销毁。而eax内存放的是调用函数在堆栈内开辟的结构体空间的指针。 调用函数利用eax内的指针处理结构体。将堆栈内的结构体值赋给其它内存变量。此处出现了临时变量。影响了效率(赋值花费时间)。 |
|