#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;
}
In the above code, the variable val
in if block is destroyed, so in return (*ptr + 1)
, the value *ptr
should be undefined, but result of this program is 5
.
I know this is a undefined program, but it seems that it produces a expected value, why?
As already stated in the comments, it is undefined behaviour - so anything can happen.
However, technically, the reason is that the stack frame is not changed after leaving the if
block, and that the compiler allocates all required local variables for the whole function at the beginning of the function, instead of creating a new stack frame for each scope. You can see this in the assembly code which is produced by your function:
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
Throughout the whole function, the stack frame layout is
-20(%rbp) => parameter val
-16(%rbp) => ptr
-4(%rbp) => variable val inside the `if` block
Note that nothing prevents the compiler from reusing -4(%rbp)
if you declare a new variable inside another scope at a later point inside your function:
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);
}
If you compare the previous assembly output with the one which is generated with the additional block in place, the only difference are these additional lines:
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With