08 ; 0000006cH
; 18 : return small;
;依然用EAX保存返回值
mov eax, DWORD PTR _small$[ebp]
; 19 : }
mov esp, ebp
pop ebp
ret 0
_create_small ENDP
_TEXT ENDS
;call_mall函数
PUBLIC _call_small
_TEXT SEGMENT
_small_obj$ = -4
_call_small PROC NEAR
; 22 : {
push ebp
mov ebp, esp
push ecx
; 23 : small_t small_obj = create_small();
call _create_small
;从eax寄存器中取出返回值填充到small_obj变量中
mov DWORD PTR _small_obj$[ebp], eax
; 24 : }
mov esp, ebp
pop ebp
ret 0
_call_small ENDP
_TEXT ENDS
2、结构体超过4字节但不等于8字节时,调用者将首先在栈上分配一块能容纳结构体的临时内存块,然后在传递完函数参数后将该临时内存块的首地址作为隐含的第一个参数最后(因为压栈顺序是从右到左)压栈,接下的动作同前所述。当被调用函数返回时,它会通过第一个隐含参数寻址到临时内存块并将返回值拷贝到其中,然后将保存有返回值内容的临时内存块的首址存进eax寄存器中,最后退出。请看下例:
typedef struct fool_t
{
short a;
short b;
short c;
} fool_t;
fool_t create_fool(short num)
{
fool_t fool = {num,num,num};
return fool;
}
void call_fool(void)
{
fool_t fool_obj = create_fool(2006);
}
汇编码为:
; create_fool函数
PUBLIC _create_fool
_TEXT SEGMENT
_num$ = 12
_fool$ = -8
; 编译器隐含传递的第一个参数(相对于stack frame基址)的偏移值,
; 该参数等于用于传递结构体返回值的临时内存块的首地址。
; $T480是编译器自动生成的标号。
$T480 = 8
_create_fool PROC NEAR
; 9 : {
push ebp
mov ebp, esp
sub esp, 8
; 10 : fool_t fool = {num,num,num};
mov ax, WORD PTR _num$[ebp]
mov WORD PTR _fool$[ebp], ax
mov cx, WORD PTR _num$[ebp]
mov WORD PTR _fool$[ebp+2], cx
mov dx, WORD PTR _num$[ebp]
mov WORD PTR _fool$[ebp+4], dx
; 11 : return fool;
; 将临时内存块的首地址存入eax
mov eax, DWORD PTR $T480[ebp]
; 将结构体返回值的低4字节通过ecx存入临时内存块的低4字节
mov ecx, DWORD PTR _fool$[ebp]
mov DWORD PTR [eax], ecx
; 将结构体返回值的高2字节通过dx存入临时内存块的高2字节
mov dx, WORD PTR _fool$[ebp+4]
mov WORD PTR [eax+4], dx
; 将临时内存块的首地址存入eax,并准备退出
mov eax, DWORD PTR $T480[ebp]
; 12 : }
mov esp, ebp
pop ebp
ret 0
_create_fool ENDP
_TEXT ENDS
; call_fool函数
PUBLIC _call_fool
_TEXT SEGMENT
_fool_obj$ = -8
; 编译器为接纳结构体返回值而自动在栈上临时分配了一块内存,
; 注意fool_t结构体大小虽为6字节,但需要对齐到4字节边界,
; 所以分配了8字节大小的空间。
$T482 = -16
_call_fool PROC NEAR
; 15 : {
push ebp
mov ebp, esp
sub esp, 16 ; 00000010H
; 16 : fool_t fool_obj = create_fool(2006);
push 2006 ; 000007d6H
; 取得临时内存块的首地址并压栈
lea eax, DWORD PTR $T482[ebp]
push eax
; 函数调用完毕后,临时内存块将被填入结构体返回值
call _create_fool
add esp, 8
; 通过ecx将临时内存块的低4字节数据存进fool_obj的低4字节
mov ecx, DWORD PTR [eax]
mov DWORD PTR _fool_obj$[ebp], ecx
; 通过dx将临时内存块的高2字节数据存进fool_obj的高2字节
mov dx, WORD PTR [eax+4]
mov WORD PTR _fool_obj$[ebp+4], dx
; 17 : }
mov esp, ebp
pop ebp
ret 0
_call_fool ENDP
_TEXT ENDS
3、结构体大小刚好为8个字节时编译器不再于栈上分配内存,而直接同时使用EAX和EDX两个寄存器传递返回值,其中EAX保存低4字节数据,EDX保存高4字节数据。请看下面2个函数:
/* 如果编译器的最大对齐模数是8,则该结构体大小为8字节 */
typedef struct big_t
{
char m1;
long m2;
} big_t;
big_t create_big(char c)
{
big_t big = {c, 2006};
return big;
}
void call_big(void)
{
big_t big_obj = create_big('A');
}
编译出的汇编码是:
; create_big函数
PUBLIC _create