if块中返回一个自动值?

时间:2014-01-22 08:19:42

标签: 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;
}

在上面的代码中,if块中的变量val被销毁,因此在return (*ptr + 1)中,值*ptr应该是未定义的,但此程序的结果是{{1} }。

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

4 个答案:

答案 0 :(得分: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

在整个函数中,堆栈框架布局为

-20(%rbp)  => parameter val
-16(%rbp)  => ptr
 -4(%rbp)  => variable val inside the `if` block

请注意,如果您在函数内部稍后的某个范围内声明了一个新变量,则无法阻止编译器重用 -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);
}

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

    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:

答案 1 :(得分:0)

我没有看到val被破坏,只是改为4.

然后给ptr值4

所以4 + 1 = 5

也许我完全错过了一些东西。

答案 2 :(得分:-1)

函数将(* ptr + 1)计算为5,并返回5.此处未定义任何内容。如果您想要未定义的行为,请返回指针,并通过main中的指针访问内存。

答案 3 :(得分:-1)

如果你检查每个指针的地址,你可以注释不同的地址指针:

    #include <stdio.h>

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

        if(val == 0)
        {
            int val = 4;
            ptr = &val;
        }
        printf("ptr @ = %p \n",ptr);
        printf("val @ = %p \n",&val);
        return (*ptr + 1);
    }

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

==> 
    result :
    ptr @ = 0x7fff8b480874 
    val @ = 0x7fff8b48086c 
    5  

==&GT;是未定义的行为,但指针地址之间的差异可以解释为什么变量“i”具有良好的值