5
#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?

Charles0429
  • 1,406
  • 5
  • 15
  • 31
  • 1
    The value of `*ptr` is indeterminate, not undefined. It's your program that's undefined :) – legends2k Jan 22 '14 at 08:21
  • I know this program is a undefined program, but it seems that it produces the expected ouput, why? – Charles0429 Jan 22 '14 at 08:22
  • 3
    Then you don't understand the meaning/gravity of `undefined`. It really means anything can happen and whatever you see is happening is just an illusion. Please read [this](http://stackoverflow.com/q/2397984/183120). – legends2k Jan 22 '14 at 08:22
  • 1
    @Charles0429 By luck. The stack isn't used in-between, so the old value is kept. If you are on an embedded system or on DOS with interrupts or have a signal handler installed and you get a signal, things might look different. – glglgl Jan 22 '14 at 08:23
  • @Charles0429 "it seems that it produces the expected output". So if it seems to be correct, then it can't possibly be incorrect? – Brandin Jan 22 '14 at 13:00

4 Answers4

2

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:
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
  • Excellent answer. May I ask how you got the assembly code? Is that done within your IDE? – DeFeNdog Jan 22 '14 at 09:26
  • 1
    With `gcc`, you simply pass the `-S` flag to the compiler: `gcc -S -o test.s test.c` produces the assembly output in `test.s` (for some reason, I was using `g++` for my answer, but that does not change much besides the function name mangling) – Andreas Fester Jan 22 '14 at 09:29
  • @Andreas I declare a new variable after the if block, but the `-4(%rbp)` is not reused, how could I make the program reuse the `-4(%rbp)`? – Charles0429 Jan 22 '14 at 09:43
  • @Andreas I also tried to declare a new variable in a new if block after the first one, but the `-4(%rbp)` is still not reused. – Charles0429 Jan 22 '14 at 09:51
  • Well, since it is still `undefined behaviour` , it depends on your compiler and on your environment whether it is reused - when compiling with gcc 4.6.3 on x86_64, it is reused – Andreas Fester Jan 22 '14 at 09:54
0

I don't see val being destroyed, simply changed to 4.

ptr is then given the value of 4

So 4+1 = 5

Maybe I'm missing something completely.

DeFeNdog
  • 1,156
  • 1
  • 12
  • 25
  • You are missing the fact that val is declared twice, once as parameter and once as local variable within the `if` block. – Andreas Fester Jan 22 '14 at 08:48
  • ptr is assigned and returned within that scope. I could see your point if you were trying to print out val itself outside of the scope. – DeFeNdog Jan 22 '14 at 08:53
  • But that is what he actually does - `ptr` points to the `int val = 4` variable which is out of scope after leaving the `if` block, hence it is pointing to undefined memory after leaving the `if` block. Whether this works is undefined, and technically depends on the compiler, the CPU type, the optimization level and probably more – Andreas Fester Jan 22 '14 at 08:57
  • Isn't he returning by value and not by reference? – DeFeNdog Jan 22 '14 at 08:59
  • It has nothing to do with the return value of the function - that is fine. The problematic part is where `ptr` is dereferenced (in `(*ptr + 1);`) - here, `*ptr` is returning an undefined value – Andreas Fester Jan 22 '14 at 09:03
-1

The function evaluates (*ptr + 1) to 5, and returns the 5. Nothing undefined here. If you want undefined behaviour, return the pointer, and access the memory through the pointer in main.

Guntram Blohm
  • 9,667
  • 2
  • 24
  • 31
-1

if you check the address of each pointer you can remark that is not the same address pointer:

    #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  

==> is undefined behaviour but the difference between address of pointer can explain why the variable "i" have the good value

Anis_Stack
  • 3,306
  • 4
  • 30
  • 53
  • **Please** consider that **val is defined twice**!!!! The val address you are printing is the one from the parameter, not from the one where `ptr` is pointing to. – Andreas Fester Jan 22 '14 at 09:01
  • why consider that ? when the @ of ptr and val are not the same – Anis_Stack Jan 22 '14 at 09:04
  • Yes, they are not the same, but that does not answer the question. The `val` **parameter** has nothing to do with the scope issue - it could equally well be named `xyz` or whatever with the same result. See http://ideone.com/kiU628 and note that in that case, it does **NOT** print `5` but (for my run) `-1217749003` since they seem to use a different compiler. – Andreas Fester Jan 22 '14 at 09:07
  • I said maybe answer the question. because is undefined behaviour – Anis_Stack Jan 22 '14 at 09:12
  • I saw your answer , in the stack frame layout you display the address of pointer and are not the same (offsets are different ) – Anis_Stack Jan 22 '14 at 09:15