if块中返回一个自动值?

Cha*_*429 5 c

#include <stdio.h>

static int test(int val)
{
    int *ptr;

    if(val == 0)
    {
        int val = 4;
        ptr = &val;
    }

    return (*ptr + 1);
}

int main(void)
{
    int i = test(0);
    printf("%d\n", i);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,valif块中的变量被破坏,因此return (*ptr + 1),该值*ptr应该是未定义的,但是该程序的结果是5.

我知道这是一个未定义的程序,但它似乎产生了预期的价值,为什么?

And*_*ter 2

正如评论中已经指出的,这是未定义的行为 - 所以任何事情都可能发生。

然而,从技术上讲,原因是离开块后堆栈帧没有改变if,并且编译器在函数开始时为整个函数分配所有所需的局部变量,而不是为每个作用域创建一个新的堆栈帧。您可以在函数生成的汇编代码中看到这一点:

ZL4testi:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp          ; set up base pointer for stack frame
                                    ; NOTE: The stack pointer is not modified here
                                    ; since you are not calling any other function
                                    ; from within `test`
        .cfi_def_cfa_register 6
        movl    %edi, -20(%rbp)     ; parameter was passed in %edi, store it in the frame

; if (parameter val == 0)
        cmpl    $0, -20(%rbp)       
        jne     .L2

; Here the scope of the `if` block starts - no changes to the stack frame setup!
; {
;    int val = 4
        movl    $4, -4(%rbp)        ; val is at -4(%rbp)

;    ptr = &val;
        leaq    -4(%rbp), %rax      ; get address of val into %eax
        movq    %rax, -16(%rbp)     ; store address of val into ptr
; }
.L2:
        movq    -16(%rbp), %rax     ; Here, ptr is still containing the address 
                                    ; of val within the stack frame
        movl    (%rax), %eax        ; load `val` from the stack even though it is out of scope
        addl    $1, %eax

        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
Run Code Online (Sandbox Code Playgroud)

纵观整个函数,栈帧布局是

-20(%rbp)  => parameter val
-16(%rbp)  => ptr
 -4(%rbp)  => variable val inside the `if` block
Run Code Online (Sandbox Code Playgroud)

请注意,如果您稍后在函数内的另一个作用域内声明一个新变量,则没有什么可以阻止编译器重用 -4(%rbp)

static int test(int val) {
    int *ptr;

    if(val == 0) {
        int val = 4;
        ptr = &val;
    }
    if(val == 0) {
        int otherval = 6;
        ptr = &otherval;
    }
    return (*ptr + 1);
}
Run Code Online (Sandbox Code Playgroud)

如果将之前的汇编输出与使用附加块生成的输出进行比较,唯一的区别是这些附加行:

    cmpl    $0, -20(%rbp)
    jne .L3

    movl    $6, -4(%rbp)      ; here, -4(%rbp) is reused for otherval
    leaq    -4(%rbp), %rax
    movq    %rax, -16(%rbp)
.L3:
Run Code Online (Sandbox Code Playgroud)