2014-10-05 109 views
2

考慮一個主文件,以及另一個實現數據結構的文件(比如:鏈表)。數據結構實現是否可以知道它是否在堆上?

鏈表的調用者可以將對象放在堆棧或堆上的鏈表上,我認爲這是調用者的責任。

所以當實現鏈表時,它是如何知道它是否在堆上?考慮從列表中移除節點的典型「方法」。鏈表如何知道它是否應該釋放內存?根據我的理解,釋放堆棧上的東西會導致未定義的行爲。

因爲這是一個類項目的一部分,我無法傳遞一些東西(isOnHeap)來表明調用者是否把內存放在堆上(澄清:因爲在我們的實現中不能這樣做) ,所以我假設這個問題可能有一個共同的解決方案,特別是考慮它會有多普遍。請注意,鏈表實現必須處理自己的內存釋放(假設這是由於它的實現對調用者隱藏而給出的)。

+1

如果這是數據 - 在某種程度上重要結構代碼(但是爲什麼?),那麼應該從頭開始設計API。 – user2864740 2014-10-05 02:16:40

+0

該標記指示這是一個與C相關的問題(在標題中添加了C)。我確實看到了這個問題。但是,請考慮問題的背景。 – 2014-10-05 02:24:59

回答

1

下面是簡單的is_on_stack(地址)功能:

int *stack_start = nullptr; 

bool is_on_stack(void* ptr) { 
    int stack_probe; 
    if(stack_start < &stack_probe ) 
    return stack_start < ptr && ptr < &stack_probe; // stack grows upwards; 
    else 
    return &stack_probe < ptr && ptr < stack_start; // stack grows downwards; 
} 

int main() { 
    int stack_probe = 0; 
    stack_start = &stack_probe; 

    int dummy; 

    bool r1 = is_on_stack(&dummy); // shall be true 

    int* heap_thing = (int*)malloc(sizeof(int)); 

    bool r2 = is_on_stack(heap_thing); // shall be false   
} 

這裏的關鍵點是,這些線路在main()函數的開頭:

int stack_probe = 0; 
    stack_start = &stack_probe; 
+0

謝謝,但我找到了一種可能的替代解決方案 - 所以我要重新問一些更具體的東西。因爲這在技術上可以回答這個問題,所以我會選擇它作爲任何其他可能發現它有用的答案。 – 2014-10-05 02:55:31

+0

@ c-smile - 我認爲你的代碼有錯誤。在'is_on_stack'中,你將'stack_probe'分配爲一個'int',然後將它的(未定義的)內容與'int'指針'stack_start'進行比較。這顯然是錯誤的。我想你想在這個函數的所有測試中使用'&stack_probe'而不是'stack_probe'。 – DoxyLover 2014-10-05 03:56:42

+0

另外,糾正我,如果我錯了,我相信這是每個​​C規範未定義的行爲。您正在比較指向不同對象的指針。雖然我同意這應該適用於大多數C實現,但我覺得我需要指出這一點。 – DoxyLover 2014-10-05 03:59:33

1

C中的數據結構實現不應該釋放任何東西,除非明確告知這樣做。典型的鏈表實現可能具有「創建節點」,「將節點插入到列表中」,「從列表中刪除節點」和「銷燬節點」等功能。這些都不需要檢查堆棧與堆。通常情況下,唯一可能在堆棧上的是整個列表的頭部指針&;即使你也可以通過你設計的API函數來分配和釋放(並使用堆)。

如果你真的想能夠建立一個入侵鏈接列表,其中節點可以保存在棧中,我們也可以談論這一點,但鑑於你發佈的背景,我懷疑是這種情況。

無論如何,沒有便攜式的解決方案可以知道堆棧或堆上是否有東西,但是您可以在這裏看到一些特定於平臺的技巧:How to know if a pointer points to the heap or the stack? - 請注意,對於班級作業而言,需要這些。

+0

然後我有點困惑,因爲我們學習釋放內存的一個典型例子是確保鏈接列表結構遍歷並釋放所有分配的內存。如果列表實現對調用者隱藏,那麼調用者將如何知道如何釋放已在堆上分配的節點上的任何數據?順便提一下,我不是在質疑你的觀點,這是一個真正的問題。對我來說,呼叫者應該承擔責任確實更有意義。 但是,我們在列表實現中給出的「remove」和「destroy」函數指示釋放動態內存。 – 2014-10-05 02:44:30

1

...我無法通過的東西(isOnHeap)指示呼叫者是否已經把內存堆上

這作物起來有時卻沒有真正在這方面。由於運行時庫的不同版本,問題通常會顯示出來。問題是一個庫分配一個調用者必須釋放的塊。

這個問題的一個例子可以在Windows Net API函數中看到。例如,假設您調用NetGroupEnum function來枚舉組。圖書館將分配GROUP_INFO_*結構。呼叫者完成結構後,呼叫者有責任撥打圖書館的NetApiBufferFree function

在這種情況下,項目需要提供分配器和刪除器功能。然後你的例程簡單地分配其他人提供的例程;然後在不再需要對象時使用例程刪除。

爲了解決最初的問題,通常可以梳理出分配的位置。但堆棧與堆通常無關緊要。

1

通常,鏈接列表節點是動態創建的,並將分配在堆中。但是,如果您選擇將已經在堆棧中的節點放入列表中,則會違反此假定。我假設你問這種情況。

然後,取決於庫的實現,free可能什麼都不做,只是註冊那些在堆中可用的內存。如果內存恰好在堆棧中,它可以選擇做任何事情。或者它可能會給出錯誤並退出。但它是不確定的。

+2

通常 - 是的。但有時在堆棧上使用數據會更有效。在某些情況下,完全相同的列表可能會混合使用項目 - 堆和堆棧。通常項目知道如何摧毀自己(它有參考銷燬功能或一些如此)... – 2014-10-05 02:35:01

相關問題