1

I am learning structures and linked-lists. However I am facing a problem that prevents me from debugging my program's bugs, since it comes, seemingly, from the function printf, which is what I use to debug the program.

The following program works fine:

struct pointer_struct
{
    struct new_struct *ptr;
};

struct new_struct
{
    int i;
    struct new_struct *ptr;
};

void init(struct pointer_struct *pointer, int nb)
{
    struct new_struct my_struct;
    my_struct.i = nb;
    my_struct.ptr = NULL;
    pointer->ptr = &my_struct;
}

int main(void)
{
    struct pointer_struct pointer;
    pointer.ptr = NULL;

    init(&pointer, 15);
    //printf("pointer.ptr = %p\n", pointer.ptr);
    printf("pointer.ptr->i = %d\n", pointer.ptr->i);
}

Output:

pointer.ptr->i = 15

But as soon as I uncomment the commented line, i takes weird values. Here are some examples of outputs:

$./a.out
pointer.ptr = 0x7fffc6bcc650
pointer.ptr->i = -448723664
$./a.out
pointer.ptr = 0x7fffd09ed480
pointer.ptr->i = 1218512176
$./a.out
pointer.ptr = 0x7ffff630fa70
pointer.ptr->i = -1073674960

What is going wrong with printf?

nounoursnoir
  • 671
  • 2
  • 10
  • 25
  • 5
    `init(struct pointer_struct *pointer, int nb)` uses a local variable `struct new_struct my_struct;` in `pointer->ptr = &my_struct;` which is _pointless_ (bad pun) after the function ends. – chux - Reinstate Monica Jun 13 '17 at 21:52
  • do you mean that the pointer `pointer.ptr`, which was given the value of the adress of this local variable, points to an adress in which *there was* a variable, which may not be here anymore because of the printf? – nounoursnoir Jun 13 '17 at 21:55
  • 2
    *points to an adress in which there was a variable* Yes. *which may not be here anymore* Exactly. *because of the printf* No, not because of the printf. Because the function where the variable used to be has returned. – n. m. could be an AI Jun 13 '17 at 21:58
  • Compile with all warnings & debug info, e.g. using `gcc -Wall -Wextra -g` with [GCC](http://gcc.gnu.org/). Then **use the debugger `gdb`** and [valgrind](http://valgrind.org/) – Basile Starynkevitch Jun 13 '17 at 21:58

2 Answers2

4

You have an undefined behavior or UB, which is always A Bad Thing™.

void init(struct pointer_struct *pointer, int nb)
{
    struct new_struct my_struct;
    my_struct.i = nb;
    my_struct.ptr = NULL;
    pointer->ptr = &my_struct;
} // here my_struct lifetime is finish so pointer->ptr become invalid

int main(void)
{
    struct pointer_struct pointer;
    pointer.ptr = NULL;

    init(&pointer, 15);
    printf("pointer.ptr = %p\n", pointer.ptr); // pointer.ptr is not valid so it's UB
    printf("pointer.ptr->i = %d\n", pointer.ptr->i); // This is UB too
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • you said "pointer.ptr is not valid so it's UB". pointer.ptr was assignated an adress. Even if the function that used this adress has finished, shouldn't the pointer always point to the adress, even though there may be anything in it? – nounoursnoir Jun 13 '17 at 21:59
  • Function `init` does not assign any value to `struct pointer_struct *pointer` – Weather Vane Jun 13 '17 at 22:01
  • @nounoursnoir "Even if the function that used this adress has finished, shouldn't the pointer always point to the adress, even though there may be anything in it?", no. – Stargateur Jun 13 '17 at 22:01
  • @WeatherVane wut? how? – nounoursnoir Jun 13 '17 at 22:07
  • 1
    @Stargateur I'd call that _dangerous behaviour_, it's a much better name. :) – Michaël Roy Jun 13 '17 at 22:20
3

You initialize pointer.ptr with a local variable.

void init(struct pointer_struct *pointer, int nb)
{
    struct new_struct my_struct;
    my_struct.i = nb;
    my_struct.ptr = NULL;
    pointer->ptr = &my_struct; // MISTAKE!!!  my_struct is on the stack.
                               // its memory space could be overwritten at 
                               // any time after the function returns.
}

Later, in main

   printf("pointer.ptr = %p\n", pointer.ptr); // This call to printf uses the stack,
                                             //  and overwrites the space used 
                                             //  by my_struct 
   printf("pointer.ptr->i = %d\n", pointer.ptr->i);
Michaël Roy
  • 6,338
  • 1
  • 15
  • 19