Caveat:这可能不完全是你所说的,但是.
如果函数返回struct
by value,则在返回过程中使用rsp/rbp
- 调用者必须在其堆栈帧中为返回值保留一个[mat]区域.
- 被调用函数的第一个参数是指向该区域的隐式/隐藏指针.
- 被调用的函数将将返回值复制到该指针指向的区域
- 然后,调用者将从临时区域复制到最终变量.
考虑以下来源:
#include <stdio.h>
struct obj {
int x[10];
};
struct obj
fill(int arg)
{
struct obj obj = {
.x = { 4, 5, 6 }
};
printf("fill: arg=%d\n",arg);
return obj;
}
void
objprint(const struct obj *obj)
{
for (int i = 0; i < 10; ++i)
printf(" %d",obj->x[i]);
printf("\n");
}
struct obj obj = {
.x = { 1, 2, 3 }
};
int
main(void)
{
objprint(&obj);
obj = fill(37);
objprint(&obj);
return 0;
}
为了清楚起见,我们用-m32
来编译,但x86_64
的原理相同.我们用:
cc \
-fno-inline-small-functions \
-fno-inline-functions-called-once \
-fno-inline-functions \
-fomit-frame-pointer \
-S -fverbose-asm -O2 -m32 retval2.c
这是[编辑]汇编器输出.堆栈框架之间有相当多的移动.
.file "retval2.c"
.text
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "fill: arg=%d\n"
.text
.p2align 4,,15
.globl fill
.type fill, @function
fill:
.LFB11:
.cfi_startproc
pushl %ebx #
.cfi_def_cfa_offset 8
.cfi_offset 3, -8
subl $16, %esp #,
.cfi_def_cfa_offset 24
# retval2.c:9: {
movl 24(%esp), %ebx # .result_ptr, .result_ptr
# retval2.c:14: printf("fill: arg=%d\n",arg);
pushl 28(%esp) # arg
.cfi_def_cfa_offset 28
pushl $.LC0 #
.cfi_def_cfa_offset 32
call printf #
# retval2.c:16: return obj;
movl $4, (%ebx) #, MEM[(struct obj *)&<retval>]
# retval2.c:17: }
movl %ebx, %eax # .result_ptr,
# retval2.c:16: return obj;
movl $5, 4(%ebx) #, MEM[(struct obj *)&<retval> + 4B]
movl $6, 8(%ebx) #, MEM[(struct obj *)&<retval> + 8B]
movl $0, 12(%ebx) #, MEM[(struct obj *)&<retval> + 12B]
movl $0, 16(%ebx) #, MEM[(struct obj *)&<retval> + 16B]
movl $0, 20(%ebx) #, MEM[(struct obj *)&<retval> + 20B]
movl $0, 24(%ebx) #, MEM[(struct obj *)&<retval> + 24B]
movl $0, 28(%ebx) #, MEM[(struct obj *)&<retval> + 28B]
movl $0, 32(%ebx) #, MEM[(struct obj *)&<retval> + 32B]
movl $0, 36(%ebx) #, MEM[(struct obj *)&<retval> + 36B]
# retval2.c:17: }
addl $24, %esp #,
.cfi_def_cfa_offset 8
popl %ebx #
.cfi_restore 3
.cfi_def_cfa_offset 4
ret $4 #
.cfi_endproc
.LFE11:
.size fill, .-fill
.section .rodata.str1.1
.LC1:
.string " %d"
.text
.p2align 4,,15
.globl objprint
.type objprint, @function
objprint:
.LFB12:
.cfi_startproc
pushl %esi #
.cfi_def_cfa_offset 8
.cfi_offset 6, -8
pushl %ebx #
.cfi_def_cfa_offset 12
.cfi_offset 3, -12
subl $4, %esp #,
.cfi_def_cfa_offset 16
# retval2.c:21: {
movl 16(%esp), %ebx # obj, ivtmp.15
leal 40(%ebx), %esi #, _16
.p2align 4,,10
.p2align 3
.L5:
# retval2.c:24: printf(" %d",obj->x[i]);
subl $8, %esp #,
.cfi_def_cfa_offset 24
pushl (%ebx) # MEM[base: _14, offset: 0B]
.cfi_def_cfa_offset 28
addl $4, %ebx #, ivtmp.15
pushl $.LC1 #
.cfi_def_cfa_offset 32
call printf #
# retval2.c:23: for (int i = 0; i < 10; ++i)
addl $16, %esp #,
.cfi_def_cfa_offset 16
cmpl %esi, %ebx # _16, ivtmp.15
jne .L5 #,
# retval2.c:25: printf("\n");
movl $10, 16(%esp) #,
# retval2.c:26: }
addl $4, %esp #,
.cfi_def_cfa_offset 12
popl %ebx #
.cfi_restore 3
.cfi_def_cfa_offset 8
popl %esi #
.cfi_restore 6
.cfi_def_cfa_offset 4
# retval2.c:25: printf("\n");
jmp putchar #
.cfi_endproc
.LFE12:
.size objprint, .-objprint
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB13:
.cfi_startproc
leal 4(%esp), %ecx #,
.cfi_def_cfa 1, 0
andl $-16, %esp #,
pushl -4(%ecx) #
pushl %ebp #
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp #,
pushl %ecx #
.cfi_escape 0xf,0x3,0x75,0x7c,0x6
subl $64, %esp #,
# retval2.c:36: objprint(&obj);
pushl $obj #
call objprint #
# retval2.c:37: obj = fill(37);
leal -56(%ebp), %eax #, tmp88
popl %edx #
popl %ecx #
pushl $37 #
pushl %eax # tmp88
call fill #
movl -56(%ebp), %eax #, tmp91
# retval2.c:38: objprint(&obj);
pushl $obj #
# retval2.c:37: obj = fill(37);
movl %eax, obj # tmp91, obj
movl -52(%ebp), %eax #, tmp93
movl %eax, obj+4 # tmp93, obj
movl -48(%ebp), %eax #, tmp95
movl %eax, obj+8 # tmp95, obj
movl -44(%ebp), %eax #, tmp97
movl %eax, obj+12 # tmp97, obj
movl -40(%ebp), %eax #, tmp99
movl %eax, obj+16 # tmp99, obj
movl -36(%ebp), %eax #, tmp101
movl %eax, obj+20 # tmp101, obj
movl -32(%ebp), %eax #, tmp103
movl %eax, obj+24 # tmp103, obj
movl -28(%ebp), %eax #, tmp105
movl %eax, obj+28 # tmp105, obj
movl -24(%ebp), %eax #, tmp107
movl %eax, obj+32 # tmp107, obj
movl -20(%ebp), %eax #, tmp109
movl %eax, obj+36 # tmp109, obj
# retval2.c:38: objprint(&obj);
call objprint #
# retval2.c:41: }
movl -4(%ebp), %ecx #,
.cfi_def_cfa 1, 0
addl $16, %esp #,
xorl %eax, %eax #
leave
.cfi_restore 5
leal -4(%ecx), %esp #,
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE13:
.size main, .-main
.globl obj
.data
.align 32
.type obj, @object
.size obj, 40
obj:
# x:
.long 1
.long 2
.long 3
.zero 28
.ident "GCC: (GNU) 8.3.1 20190223 (Red Hat 8.3.1-2)"
.section .note.GNU-stack,"",@progbits
请注意:
尽管main
做到了:obj = fill(37);
,但它传递了指向其堆栈帧上temp区域的指针,而不是传递指向lvalue obj
的指针.
呼叫者将临时区域手动复制到左值.