Pointer changes value after printf

2019-09-15 21:39发布

问题:

So today I was trying to implement simple stack in C, but I have encountered following issue that I can't resolve or explain why is happening.

#include<stdio.h>

typedef struct stack_element stack_element;
typedef struct stack stack;

struct stack_element
{
    void* data;
    stack_element* next;
};

struct stack
{
    stack_element* top;
    int size;
    int max_size;
};

void push(stack* this, stack_element to_add)
{
    if(this->top == NULL)
    {
        this->top = &to_add;
        this->top->next = this->top;
        printf("%p\n", this->top);
        printf("%p\n", this->top->next);
        printf("First\n");
        return;
    }
}

void debug(stack* this)
{
    if(this->top == NULL) return;
    printf("Sample 1 %p\n", this->top);
    printf("Sample 2 %p\n\n", this->top->next);
}

int main()
{
    stack_element* tmp = malloc(sizeof(stack_element));
    stack* st = malloc(sizeof(stack)); 
    tmp->data = 1;
    push(st, *tmp);
    printf("Sample 1 %p\n", st->top);
    printf("Sample 2 %p\n\n", st->top->next);
    debug(st);
    printf("Sample 1 %p\n", st->top);
    printf("Sample 2 %p\n\n", st->top->next);
    printf("END\n");
    return 0;
}

Code above gives some strange results, and also different ones if I change compiler.

First compiler I've tried is gcc and it gives following output:

0x7ffd6d96d3b0
0x7ffd6d96d3b0
First
Sample 1 0x7ffd6d96d3b0
Sample 2 0x7ffd6d96d3b0

Sample 1 0x7ffd6d96d3b0
Sample 2 0x400670

Sample 1 0x7ffd6d96d3b0
Sample 2 0x40068d

END

Second result is from clang:

0x7ffd6d05fb30
0x7ffd6d05fb30
First
Sample 1 0x7ffd6d05fb30
Sample 2 0x7ffd6d05fb30

Sample 1 0x7ffd6d05fb30
Sample 2 0x2042030

Sample 1 0x7ffd6d05fb30
Sample 2 0x2042030

END

My first question is why is Sample 2 even changing when I printf it from void debug? Also I have tried to comment out debug function call and results are also strange. In gcc samples are as expected all are matching, but in clang there is difference in sample 2 for no obvious reason.

My second question is why is there even difference between compilers while compiling this code?

Hope I have posted enough information about the issue I am having, if not write me in comment to post more information.

回答1:

Your push function takes the second parameter by value, so it creates a local copy of the struct stack_element. That means the line this->top = &to_add; redirects the pointer top to point at a local variable (with automatic storage duration). After the function push ends that variable's lifetime ends, changing top into a dangling pointer. Thus the dereferencing this->top->next (and st->top->next) invoke undefined behavior.

What you most likely intended to do was take the second parameter for push as a pointer: void push(stack* this, stack_element* to_add) and change the line:

this->top = &to_add;

to

this->top = to_add;

NOTE: Your suggestion of using malloc inside push is another possibility, however that might create unclear code, as it would require the caller to know that top needs to be freed in order to avoid memory leaks. Alternatively you could also provide a "destructor" function which would take care of all memory management, hiding it from the caller.