关于返回结构体的函数=
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关于返回结构体的函数
【前言】写作本文,源于最近回复的《汇编中函数返回结构体的方法》一文。在网络上也已经有一些相关文章和相关问题,有的文章已经给出了一部分结果,但总体而言还缺少比较重要的结论。本文以分析VC6 编译器,32 位架构为主来重复性分析这个话题。
(一)不超过8 bytes 的小结构体可以通过EDX:EAX 返回。
本文的范例代码取材于《汇编中函数返回结构体的方法》一文,并在此基础上进行修改和试验。要研究的第一份代码如下,定义一个不超过8 bytes 的小结构体,不超过8 bytes 是因为这个结构体能够用EDX:EAX 容纳,我们之后将看到在release 编译时,编译器能够向返回普通基础类型那样进行返回。
#include
//不超过8 bytes 的“小结构体”
struct A
{
int a;
int b;
};
//返回结构体的函数
struct A add(int x, int y)
{
struct A t;
t.a = x * y;
return t;
}
int main()
{
struct A t = add(3, 4);
printf("t.a = %ld\n", t.a);
return0;
}
首先,我们需要解决一个常见困惑,就是要明确这段代码和下面的典型错误代码的区别:
char* get_buffer()
{
char buf[8];
return buf;
}
上面的get_buffer 返回的是栈上的临时变量空间,在函数返回后,其所在的空间也就被“回收/释放”了,也就是说函数返回的地址位于栈的增长方向上,是不稳定和不被保证的。
那么返回结构体的函数则不同,你可以发现返回结构体的函数是工作正常有效的。在add 函数中有一个
临时性结构体t,毫无疑问,t 将在add 函数返回时被释放,但由于t 被当做“值”进行返回,因此编译器将保证add 的返回值对于add 的调用者(caller)来说是有效的。
另外需要明确的一点是,我个人觉得,现实里这种返回结构体的方式比较少见,后面将会看到这样做会产生临时对象和多余拷贝过程,效率不高。常见方法是传递结构体指针。但作为语言上允许的方式,有必要弄清楚编译器如何实现这种方式,而要弄清楚这个问题,需要查看汇编代码。使用VC6 输入上述代码,下面分别给出其汇编代码。
(1)debug 版本,汇编代码如下。
.text:00401020add proc near ; CODE XREF: j_addj
.text:00401020
.text:00401020 var_48 = dword ptr -48h
.text:00401020 var_8 = dword ptr -8
.text:00401020 var_4 = dword ptr -4
.text:00401020 arg_0 = dword ptr 8
.text:00401020 arg_4 = dword ptr 0Ch
.text:00401020
.text:00401020push ebp
.text:00401021mov ebp, esp
.text:00401023sub esp, 48h
.text:00401026push ebx
.text:00401027push esi
.text:00401028push edi
.text:00401029lea edi, [ebp+var_48]
.text:0040102C mov ecx, 12h
.text:00401031mov eax, 0CCCCCCCCh
.text:00401036rep stosd
.text:00401038mov eax, [ebp+arg_0]
.text:0040103B imul eax, [ebp+arg_4]
.text:0040103F mov[ebp+var_8], eax
.text:00401042mov eax, [ebp+var_8]
.text:00401045mov edx, [ebp+var_4]
.text:00401048pop edi
.text:00401049pop esi
.text:0040104A pop ebx
.text:0040104B mov esp, ebp
.text:0040104D pop ebp
.text:0040104E retn
.text:0040104E add endp
.text:0040104E
.text:0040104E; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
.text:0040104F dd 4 dup(0CCCCCCCCh)
.text:0040105F align 10h
.text:00401060
.text:00401060; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹? .text:00401060
.text:00401060; Attributes: bp-based frame
.text:00401060
.text:00401060 main proc near ; CODE XREF: j_mainj
.text:00401060
.text:00401060 var_50 = dword ptr -50h
.text:00401060 var_10 = dword ptr -10h
.text:00401060 var_C = dword ptr -0Ch
.text:00401060 var_8 = dword ptr -8
.text:00401060 var_4 = dword ptr -4
.text:00401060
.text:00401060push ebp
.text:00401061mov ebp, esp
.text:00401063sub esp, 50h
.text:00401066push ebx
.text:00401067push esi
.text:00401068push edi
.text:00401069lea edi, [ebp+var_50]
.text:0040106C mov ecx, 14h
.text:00401071mov eax, 0CCCCCCCCh
.text:00401076rep stosd
.text:00401078push4
.text:0040107A push3
.text:0040107C call j_add
.text:00401081add esp, 8
.text:00401084mov[ebp+var_10], eax
.text:00401087mov[ebp+var_C], edx
.text:0040108A mov eax, [ebp+var_10]
.text:0040108D mov[ebp+var_8], eax
.text:00401090mov ecx, [ebp+var_C]
.text:00401093mov[ebp+var_4], ecx
.text:00401096mov edx, [ebp+var_8]
.text:00401099push edx
.text:0040109A push offset ??_C@_0L@CMGB@t?4a?5?$DN?5?$CFld?6?$AA@ ; "t.a = %ld\n"
.text:0040109F call printf
.text:004010A4add esp, 8
.text:004010A7xor eax, eax
.text:004010A9pop edi
.text:004010AA pop esi
.text:004010AB pop ebx
.text:004010AC add esp, 50h
.text:004010AF cmp ebp, esp
.text:004010B1call__chkesp
.text:004010B6mov esp, ebp