#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.
我知道这是一个未定义的程序,但它似乎产生了预期的价值,为什么?
正如评论中已经指出的,这是未定义的行为 - 所以任何事情都可能发生。
然而,从技术上讲,原因是离开块后堆栈帧没有改变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)