2016-12-01 72 views
-1

在測試我的堆棧實現(即使用鏈表)時,我發現了一件有趣的事情。有一個測試代碼重現它:訪問已刪除的內存

#include <iostream> 
using namespace std; 

struct Node { 
    int val; 
    Node *prev; 
}; 

int main() { 
    Node *first = new Node; 
    first->val = 20; 
    first->prev = NULL; 

    cout << "first:" << first << endl; 

    Node *p = new Node; 
    p->val = 40; 
    p->prev = first; 

    delete p; 

    cout << "p->val:" << p->val << endl; 
    cout << "p->prev:" << p->prev << endl; 
} 

輸出:

first:0x22cfc20 
p->val:0 
p->prev:0x22cfc20 


但是,如果我換元素的順序在struct Node這樣的定義:

struct Node { 
    Node *prev; 
    int val; 
}; 

輸出將爲

first:0x195dc20 
p->val:40 
p->prev:0 


當然這是在兩種情況下,不確定的行爲,但也許它存在着一些合理的解釋爲什麼是這樣工作的?或者它只是隨機的?我試着多次運行代碼,但每次都輸出相同的輸出(除了特定的地址值)並且從不壓碎。

+2

未定義的行爲並不真正_interesting_。 –

+1

「未定義的行爲」表示「未定義的行爲」。您觀察到的實際行爲在很大程度上取決於您的實現的堆管理例程。而且由於您甚至不打算指定您的操作系統,因此無法在此處收集更多信息。 –

+3

只要您更改編譯器選項(如優化),您的程序就可能開始有不同的表現。那麼你會看到圍繞未定義行爲的原因追逐的無用功。在某個時候,你意識到這是浪費時間,你有更好的事情要做(如開發你的應用程序)。 – PaulMcKenzie

回答

2

正如其他人所說的,我們很少能夠明確地說出這種情況,不僅因爲未定義的行爲,而且因爲我們對編譯的平臺一無所知。

但也有幾件事情,可以說:

在struct更改成員的順序可能會導致編譯到構件之間或在該結構的端部,以便滿足應用不同的填充任何對齊規則目前正在編譯。

如果沒有在這種情況下發生的,那麼它可能會影響在堆中分配的內存中的佈局,並因此影響到哪些數據恰好又被重寫堆空間被釋放,然後再利用。這可能可以解釋爲什麼結果不同。

2

未定義的行爲根本沒有指定任何具體的行爲在所有。可以允許執行程序,或關閉計算機,格式化硬盤,在貓的內部打開黑洞,甚至在黑洞內打開貓。

如果程序試圖在內存上執行無效操作,大多數實現將暫停執行。有時候,如果你不走運,你的程序就可以正常工作,就像你的例子一樣。

這樣說:有幾百個原因可能會導致您刪除內存,但對此行爲的任何假設都是無效的,並且可能因任何原因導致出現不同結果。