2017-03-11 60 views
0

所以今天我試圖在C中實現簡單堆棧,但遇到以下問題,我無法解決或解釋爲什麼會發生。指針在printf後更改值

#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; 
} 

上面的代碼給出了一些奇怪的結果,如果我改變編譯器也有不同的結果。

第一個編譯器我已經試過是gcc,它給下面的輸出:

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

Sample 1 0x7ffd6d96d3b0 
Sample 2 0x400670 

Sample 1 0x7ffd6d96d3b0 
Sample 2 0x40068d 

END 

第二個結果是從鐺:

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

Sample 1 0x7ffd6d05fb30 
Sample 2 0x2042030 

Sample 1 0x7ffd6d05fb30 
Sample 2 0x2042030 

END 

我的第一個問題是,爲什麼是樣品2甚至改變時我從void調試printf它?另外我也試過註釋調試函數調用,結果也很奇怪。在海灣合作委員會樣品如預期所有匹配,但在鐺中有沒有明顯的原因樣品2有差異。

我的第二個問題是爲什麼在編譯此代碼時編譯器之間有甚至不同?

希望我發佈了足夠的有關我遇到的問題的信息,如果不寫信給我發表評論以發佈更多信息。

+1

你的'push'函數改變'this-> top'指向棧上的一個值。這個值在函數結束之後超出了範圍(結束它的生命週期)創建一個懸掛指針,所以通過'this-> top-> next'來解除引用* undefined behavior * – UnholySheep

+0

那麼,malloc新變量是個好主意,附加到這個 - >頂部? – acac97

+0

我通過一個答案添加了一些解釋 - 我希望能夠說清楚 – UnholySheep

回答

3

您的push函數按值取第二個參數,因此它創建了struct stack_element的本地副本。這意味着行this->top = &to_add;重定向指針top指向一個本地變量(具有自動存儲持續時間)。在函數push結束變量的生命週期結束後,將top更改爲懸掛指針。因此,取消引用this->top->next(和st->top->next)調用未定義的行爲

什麼是你最有可能打算做的是採取的第二個參數爲push爲指針:void push(stack* this, stack_element* to_add)並更改行:

this->top = &to_add; 

this->top = to_add; 

注意:您使用malloc的建議在push內部是另一種可能性,但是這可能會產生不明確的代碼,因爲它要求調用者知道top需要爲freed以避免內存泄漏。或者,你也可以提供一個「析構函數」功能,它將處理所有的內存管理,將其從調用者中隱藏起來。

+0

感謝奇妙的回答UnholySheep。你提出的解決方案就像魅力一樣。最重要的是,我學到了一些東西。再次感謝:D – acac97