2008-11-12 107 views
6

是否有可能在當前函數退出之前顯式釋放由C的alloca()分配的內存?如果是這樣,怎麼樣?釋放alloca分配的內存

+0

你能解釋一下你的動機嗎?爲什麼你想在返回之前釋放分配的空間? – Motti 2008-12-07 20:22:08

回答

8

這是可能的,但沒有預先寫好的功能。你必須深入研究你的編譯器的alloca()實現,找出它在做什麼,然後編寫你自己的freea()。由於每個編譯器都以不同的方式執行alloca(),所以必須爲每個編譯器重寫freea()。

但我覺得很難相信這是值得的麻煩。如果需要明確釋放它,只需使用malloc/free,這些函數通常會進行大量優化。利用它們。

+3

一個可移植的實現是「void freea(void * p){} //只是僞造它」。 – paxdiablo 2008-11-12 06:49:06

1

不,因爲它與本地變量一起被分配在堆棧上。如果你想要顯式地釋放內存,可以使用其中一種動態內存分配函數。

有沒有混合允許你明確地自由讓它在函數退出時自動釋放,至少不在標準中。

2

您正在使用alloca()分配堆棧。如果事後發生了其他事情(如果沒有在彙編中編寫所有東西,你無法控制它),你不能只收縮堆棧。所以直到你離開函數的堆棧框架,這是不可能的。

這也是爲什麼你可以真正搞砸了,如果你溢出分配的緩衝區。你可以開始覆蓋函數返回的代碼地址,使它跳轉到其他地方,各種可怕的東西。小心!

Malloc在堆上工作,所以這就是爲什麼它可以做的更靈活。

1

這對連續傳球風格(CPS),而不是realloca很有用。

在將棧縮回到字符串的長度並調用下一個函數之前,可以調用一個函數來分配和操作棧頂的一個字符串。

沒有概念上的原因,爲什麼不能有一個freea(),除了堆棧中最頂層的入口外,其他任何東西都是nop。

2

使用C99,您可以使用Variable Length Array實現同樣的功能。只需在新的範圍內宣佈VLA;它會在範圍退出時自動釋放。

例如:

int some_function(int n) { 
    // n has the desired length of the array 
    ... 
    { // new scope 
     int arr[n]; // instead of int *arr = alloca(n*sizeof(int)); 
     // do stuff with array 
    } 
    // function continues with arr deallocated 
    ... 
} 
1

是的,但是這取決於ALLOCA的實現()。 alloca()的一個合理而簡單的實現是通過調整堆棧指針,將新分配的塊放置在棧頂的上。因此,要釋放此內存,我們只需要做一個分配(但你需要學習真正落實了alloca()的),讓我們通過以下非便攜代碼,例如驗證:

#include <stdio.h> 
#include <alloca.h> 

int main() 
{ 
    unsigned long p0, p1, p2; 
    p0=(unsigned long)alloca(0); 
    p1=(unsigned long)alloca((size_t) 0x1000); 
    p2=(unsigned long)alloca((size_t)-0x1000); 
    printf("p0=%lX, p1=%lX, p2=%lX\n", p0, p1, p2); 
    return 0; 
} 

在一箇舊的x64機器鏗鏘2.9,樣品輸出爲:

p0=7FFF2C75B89F, p1=7FFF2C75A89F, p2=7FFF2C75B89F 

所以我們知道實現驗證參數-0x1 000,否則無符號值將是一個非常大的整數。堆棧指針最初是0x ... B89F;因爲這個堆棧向上增長alloca(0x1000)因此將堆棧指針向上改爲爲(0x ... B89F - 0x1000)= 0x ... A89F。負分配後(0xA89F - ( - 0x1000))堆棧指針返回0x ... B89F。

然而,用gcc 4.8.3,樣品輸出爲:

p0=7FFFA3E27A90, p1=7FFFA3E26A80, p2=7FFFA3E27A70 

在/usr/include/alloca.h裏面我們發現:

#ifdef __GNUC__ 
# define alloca(size) __builtin_alloca (size) 
#endif /* GCC. */ 

所以我們知道,內建ALLOCA由gcc 4.8.3提供的函數做了類似的事情,除了它分配額外的0x10字節作爲安全邊界。在進行負分配時,它仍然假設它向上增長,因此試圖保留0x10個額外字節( - 0x10),所以p2 = 0x ... 6A80 - ( - 0x1000) - 0x10 = 0x ... 7A70。所以,要特別小心。