2

看看这段代码。我在这里返回复合文字的地址。

#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("%d\n", baz);

    return 0;
}

static struct bar *
to_bar(int a, int b)
{
    return &(struct bar) {a, b};
}

输出:

3

ISO/IEC 9899 说:

如果复合文字出现在函数体之外,则该对象具有静态存储持续时间;否则,它具有与封闭块关联的自动存储持续时间。

即,在to_bar函数中,由复合文字创建的未命名对象具有自动存储期限。因此,它将在to_bar. 看来,这段代码会产生未定义的行为(基于标准)。是这样吗?

4

1 回答 1

1

你说的对。在您的示例中,您在 from 返回后立即检索了字段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 corrupt\n");

    int baz = corrupted_bar->a + corrupted_bar->b;

    printf("baz = %d\n", 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 = %d\n"
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
于 2020-12-10T22:12:01.817 回答