Search code examples
cpointersundefined-behavior

Unable to understand the pointer to pointer output : 15 15


Could someone please help to understand on how the output of the following code is 15 15?

#include <stdio.h>
void foo(int **p1);
int main()
{
    int i = 10;
    int *p = &i;
    foo(&p);  //address of constant pointer p passed to function
    printf("%d\n", *p);

}
void foo(int **p1)
{
    int j = 15;
    *p1 = &j; //I don't get this line
    printf("%d\n", **p1); 
}

Let's assume that addr(i)=ff2, addr(pointer p)=ff4, addr(j)=ff6

i=10, j=15

p= address(i), so p= ff2

p1=address(p), so p1= ff4


Solution

  • Ignoring the undefined behavior1 for a minute, here's what's happening, or at least the intent of the example.

    First, you have an integer object i containing the value 10:

       +----+
    i: | 10 |
       +----+
    

    You create a pointer object p that points to i:

       +---+          +----+
    p: |   | ----> i: | 10 |
       +---+          +----+
    

    You pass the address of p to foo. The function parameter p1 points to p:

        +---+          +---+          +----+
    p1: |   | ----> p: |   | ----> i: | 10 |
        +---+          +---+          +----+
    

    Within foo you create the integer object j with the value 15:

       +----+
    j: | 15 |
       +----+
    

    Now, here's the fun part - you set p to point to j by dereferencing p1 and assigning the address of j to the result. *p1 == p, so by extension *p1 = &j is effectively the same as writing p = &j. After that line, you have the following situation:

        +---+          +---+          +----+
    p1: |   | ----> p: |   | ----> j: | 15 |
        +---+          +---+          +----+
    

    That's why printing the value of **p1 in foo outputs 15. p1 == &p, so *p1 == p == &j, so **p1 == *p == j == 15.

    Now, when the function foo exits, j ceases to exist, so p is no longer a valid pointer - it no longer points to an active object. Since it's no longer valid, the behavior on dereferencing it is undefined; your code may crash, or it may corrupt data, or it may appear to work as expected, or do something else entirely.

    The logical object j ceased to exist, but the memory it used to occupy is still there, and will contain the last thing written to it, which is the value 15. As long as nothing else overwrites that memory, you'll continue to see *p == 15. But this behavior is not guaranteed, and you should not rely on it.

    That's far from the only problem with this code, as Vlad and others have pointed out. But again, that's basically what's happening.


    1. Including but not limited to: type mismatch between the implicit declaration of foo (function returning int) and the explicit definition of foo (function returning void), attempting to write to a const-qualified pointer through a non-const expression and dereferencing an invalid pointer.