看看这段代码。我在这里返回一个复合文字的地址。
#include <stdio.h>
#define FOO(bar) ((bar)->a + (bar)->b)
struct bar {
int a;
int b;
};
static struct bar * to_bar(int a, int b);
int main(void)
{
int baz = FOO((struct bar *) {to_bar(1, 2)});
printf("%dn", baz);
return 0;
}
static struct bar *
to_bar(int a, int b)
{
return &(struct bar) {a, b};
}
输出:
3
ISO/IEC 9899规定:
如果复合文字出现在函数体之外,则对象具有静态存储持续时间;否则,它将自动与封闭块相关联的存储持续时间。
I。e.在to_bar
函数中,由复合文字创建的未命名对象具有自动存储持续时间。因此,它将在to_bar
的范围之外被破坏。这段代码似乎产生了未定义的行为(基于标准(。是这样吗?
你说得对。在您的示例中,从to_bar
返回后立即检索字段,因此您没有时间破坏已故to_bar
函数的堆栈帧。但这里有另一个例子:
struct bar {
int a;
int b;
};
static struct bar * to_bar(int a, int b);
int main(void)
{
struct bar * corrupted_bar = to_bar(1, 2);
printf("this print will corruptn");
int baz = corrupted_bar->a + corrupted_bar->b;
printf("baz = %dn", baz);
return 0;
}
static struct bar *
to_bar(int a, int b)
{
return &(struct bar) {a, b};
}
当执行时
this print will corrupt
baz = -59543507
如果你看组装
.LC0:
.string "this print will corrupt"
.LC1:
.string "baz = %dn"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov esi, 2
mov edi, 1
call to_bar ; call to_bar
mov QWORD PTR [rbp-8], rax ; save address returned to a local pointer
mov edi, OFFSET FLAT:.LC0 ; argument into puts()
call puts ; call puts(), which creates its own local variables that corrupts the bar struct
mov rax, QWORD PTR [rbp-8]
mov edx, DWORD PTR [rax]
mov rax, QWORD PTR [rbp-8]
mov eax, DWORD PTR [rax+4]
add eax, edx
mov DWORD PTR [rbp-12], eax
mov eax, DWORD PTR [rbp-12]
mov esi, eax
mov edi, OFFSET FLAT:.LC1
mov eax, 0
call printf
mov eax, 0
leave
ret
to_bar:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi
mov DWORD PTR [rbp-24], esi
mov eax, DWORD PTR [rbp-20]
mov DWORD PTR [rbp-8], eax ; field 'a' gets stored, notice dest addr rbp-8 is in the stack frame of this function (local variable)
mov eax, DWORD PTR [rbp-24]
mov DWORD PTR [rbp-4], eax ; field 'b' gets stored, same as above
lea rax, [rbp-8]
pop rbp
ret