stack不同 為什麼使用alloca()不被認為是好的做法?




malloc c用法 (20)

所有其他答案都是正確的。 但是,如果你想使用alloca()分配的東西相當小,我認為這是一種比使用malloc()或其他方法更快,更方便的好技術。

換句話說, alloca( 0x00ffffff )是危險的,可能會導致溢出,正如char hugeArray[ 0x00ffffff ]; 是。 謹慎和合理,你會沒事的。

alloca()從堆棧中分配內存,而不是malloc()堆。 所以,當我從例程返回時,內存被釋放。 所以,實際上這解決了我釋放動態分配內存的問題。 釋放通過malloc()分配的內存是一個非常頭痛的問題,如果不知何故錯過了會導致各種內存問題。

儘管有上述特徵,為什麼使用alloca()不鼓勵?


In my opinion, alloca(), where available, should be used only in a constrained manner. Very much like the use of "goto", quite a large number of otherwise reasonable people have strong aversion not just to the use of, but also the existence of, alloca().

For embedded use, where the stack size is known and limits can be imposed via convention and analysis on the size of the allocation, and where the compiler cannot be upgraded to support C99+, use of alloca() is fine, and I've been known to use it.

When available, VLAs may have some advantages over alloca(): The compiler can generate stack limit checks that will catch out-of-bounds access when array style access is used (I don't know if any compilers do this, but it can be done), and analysis of the code can determine whether the array access expressions are properly bounded. Note that, in some programming environments, such as automotive, medical equipment, and avionics, this analysis has to be done even for fixed size arrays, both automatic (on the stack) and static allocation (global or local).

On architectures that store both data and return addresses/frame pointers on the stack (from what I know, that's all of them), any stack allocated variable can be dangerous because the address of the variable can be taken, and unchecked input values might permit all sorts of mischief.

Portability is less of a concern in the embedded space, however it is a good argument against use of alloca() outside of carefully controlled circumstances.

Outside of the embedded space, I've used alloca() mostly inside logging and formatting functions for efficiency, and in a non-recursive lexical scanner, where temporary structures (allocated using alloca() are created during tokenization and classification, then a persistent object (allocated via malloc()) is populated before the function returns. The use of alloca() for the smaller temporary structures greatly reduces fragmentation when the persistent object is allocated.


alloca()malloc()特別危險的地方是內核 - 典型操作系統的內核有一個固定大小的堆棧空間,硬編碼到它的一個頭部; 它不像應用程序的堆棧那麼靈活。 以不合理的大小調用alloca()可能會導致內核崩潰。 某些編譯器在編譯內核代碼時應該打開的某些選項中警告使用alloca() (甚至是VLA) - 在這裡,最好在堆中分配內存,編碼限制。


I don't think anyone has mentioned this: Use of alloca in a function will hinder or disable some optimizations that could otherwise be applied in the function, since the compiler cannot know the size of the function's stack frame.

For instance, a common optimization by C compilers is to eliminate use of the frame pointer within a function, frame accesses are made relative to the stack pointer instead; so there's one more register for general use. But if alloca is called within the function, the difference between sp and fp will be unknown for part of the function, so this optimization cannot be done.

Given the rarity of its use, and its shady status as a standard function, compiler designers quite possibly disable any optimization that might cause trouble with alloca, if would take more than a little effort to make it work with alloca.


原因如下:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

不是說任何人都會寫這些代碼,但是你傳遞給alloca的大小參數幾乎肯定來自某種輸入,這可能會惡意地使你的程序分配一些類似的東西。 畢竟,如果大小不是基於輸入或不可能很大,為什麼不只是聲明一個小的,固定大小的本地緩衝區?

幾乎所有使用alloca和/或C99 vlas的代碼都有嚴重的錯誤,這會導致崩潰(如果幸運的話)或特權折中(如果你不那麼幸運)。


每個人都已經指出了堆棧溢出中潛在的未定義行為,但我應該提及的是,Windows環境有一個很好的機制來使用結構化異常(SEH)和守護頁來捕獲這個問題。 由於堆棧只根據需要增長,因此這些防護頁駐留在未分配的區域。 如果你分配給它們(通過溢出堆棧)拋出一個異常。

你可以捕捉到這個SEH異常,並調用_resetstkoflw來重置堆棧並繼續快樂的方式。 它不是理想的,但它是另一種機制,至少知道東西擊中粉絲時出了什麼問題。 *尼克斯可能有類似的東西,我不知道。

我建議通過封裝alloca並在內部跟踪它來限制最大分配大小。 如果你真的對它核心的話,你可以在你的函數的頂部拋出一些示波器哨兵來跟踪函數範圍內的任何alloca分配,並根據項目允許的最大金額進行檢查。

此外,除了不允許內存洩漏,alloca不會導致內存碎片,這非常重要。 如果你聰明地使用它,我不認為alloca是不好的做法,這對任何事情都基本上是正確的。 :-)


If you accidentally write beyond the block allocated with alloca (due to a buffer overflow for example), then you will overwrite the return address of your function, because that one is located "above" on the stack, ie after your allocated block.

The consequence of this is two-fold:

  1. The program will crash spectacularly and it will be impossible to tell why or where it crashed (stack will most likely unwind to a random address due to the overwritten frame pointer).

  2. It makes buffer overflow many times more dangerous, since a malicious user can craft a special payload which would be put on the stack and can therefore end up executed.

In contrast, if you write beyond a block on the heap you "just" get heap corruption. The program will probably terminate unexpectedly but will unwind the stack properly, thereby reducing the chance of malicious code execution.


IMHO, alloca is considered bad practice because everybody is afraid of exhausting the stack size limit.

I learned much by reading this thread and some other links:

I use alloca mainly to make my plain C files compilable on msvc and gcc without any change, C89 style, no #ifdef _MSC_VER, etc.

Thank you ! This thread made me sign up to this site :)


Actually, alloca is not guaranteed to use the stack. Indeed, the gcc-2.95 implementation of alloca allocates memory from the heap using malloc itself. Also that implementation is buggy, it may lead to a memory leak and to some unexpected behavior if you call it inside a block with a further use of goto. Not, to say that you should never use it, but some times alloca leads to more overhead than it releaves frome.


Processes only have a limited amount of stack space available - far less than the amount of memory available to malloc() .

By using alloca() you dramatically increase your chances of getting a error (if you're lucky, or an inexplicable crash if you're not).


一個問題是它不是標準的,儘管它得到了廣泛的支持。 在其他條件相同的情況下,我總是使用標準函數而不是通用的編譯器擴展。


答案就在man頁(至少在Linux上):

返回值alloca()函數返回一個指向分配空間開始的指針。 如果分配導致堆棧溢出,則程序行為未定義。

這並不是說它永遠不會被使用。 我工作的OSS項目之一廣泛使用它,只要你不濫用( alloca巨大的價值),就沒有問題。 一旦你過去了“幾百字節”的標記,現在是時候使用malloc和朋友了。 你仍然可能會得到分配失敗,但至少你會有一些失敗的跡象,而不是把堆棧炸掉。


Most answers here largely miss the point: there's a reason why using _alloca() is potentially worse than merely storing large objects in the stack.

The main difference between automatic storage and _alloca() is that the latter suffers from an additional (serious) problem: the allocated block is not controlled by the compiler , so there's no way for the compiler to optimize or recycle it.

Compare:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

有:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

The problem with the latter should be obvious.


我遇到的最難忘的錯誤之一是使用alloca的內聯函數。 它在程序執行的隨機點表現為堆棧溢出(因為它在堆棧上分配)。

在頭文件中:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

在實現文件中:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

所以發生了什麼事情是編譯器內嵌了DoSomething函數,並且所有的堆棧分配都發生在Process()函數內部,從而使堆棧上升。 在我的辯護(我不是那個發現這個問題的人,當我不能修復它時,我不得不去找一個高級開發人員哭),這不是直接的alloca ,它是ATL字符串之一轉換宏。

所以課程是 - 不要在你認為可能被內聯的函數中使用alloca


很多有趣的答案,這個“舊”的問題,甚至一些相對較新的答案,但我沒有發現任何提及這....

如果使用得當且小心,一致地使用alloca() (可能在整個應用程序範圍內)處理小的可變長度分配(或C99 VLA,如果可用)可以導致整體堆棧增長低於使用超大本地數組固定長度。 所以,如果你仔細使用它, alloca()可能對你的堆棧有好處

我在....中找到了這句話。好吧,我把這句話寫出來了。 但是,真的,想想看....

@j_random_hacker在其他答案中的評論中非常正確:避免使用alloca()以支持超大的本地數組,這不會讓您的程序更安全地從堆棧溢出(除非您的編譯器足夠長以允許內聯使用alloca()的函數alloca()在這種情況下,你應該升級,或者除非你在循環內部使用alloca() ,在這種情況下,你應該...不要在循環內部使用alloca() )。

我從事過桌面/服務器環境和嵌入式系統。 很多嵌入式系統根本不使用堆棧(它們甚至不支持它),原因包括動態分配內存是惡意的,因為應用程序永遠不會有內存洩漏風險每次都會重新啟動數年,或者更合理的理由認為動態內存是危險的,因為它無法確定應用程序永遠不會將其堆棧碎片化為虛假內存耗盡。 所以嵌入式程序員只剩下幾個選擇。

alloca() (或者VLA)可能只是這個工作的正確工具。

我曾經一次又一次地看到程序員在一個堆棧分配的緩衝區中“足夠大以處理任何可能的情況”。 在深度嵌套調用樹中,重複使用該(反 - ?)模式會導致堆棧過度使用。 (想像一下20級的調用樹,其中由於不同的原因,在每個級別上,該函數一般會過度分配1024字節的緩衝區以“僅僅是安全的”,通常它只使用16個或更少的緩衝區,並且僅在非常少數情況下可能會使用更多)。另一種方法是使用alloca()或VLA,並且只分配盡可能多的堆棧空間,以避免不必要的堆棧負擔。 希望當調用樹中的一個函數需要大於正常的分配時,調用樹中的其他函數仍然使用其正常的小分配,並且整個應用程序堆棧的使用量要比每個函數盲目地過度分配本地緩衝區。

但是,如果你選擇使用alloca() ...

根據本頁面上的其他答案,似乎VLA應該是安全的(如果從循環內調用,它們不會復合堆棧分配),但是如果您使用的是alloca() ,請注意不要在循環內部使用它,如果有可能在另一個函數的循環中被調用,請確保你的函數不能被內聯。


alloca()很好而且高效......但它也被深深打破了。

  • 打破範圍行為(功能範圍而不是范圍)
  • 使用與malloc不一致( alloca()- ted指針不應該被釋放,因此你必須跟踪你的指針來自哪裡free()只有你用malloc()得到的
  • 當你也使用內聯時不好的行為(根據被調用者是否被內聯,有時候調用者函數會進入調用者函數)。
  • 沒有堆棧邊界檢查
  • 未定義的行為在失敗的情況下(不像malloc一樣返回NULL ...失敗意味著什麼,因為它無論如何都不檢查堆棧邊界...)
  • 不是ansi標準

在大多數情況下,您可以使用局部變量和主體大小來替換它。 如果它用於大型對象,將它們放在堆上通常是一個更安全的想法。

如果你真的需要它,你可以使用VLA(C ++中沒有vla,太糟糕了)。 關於範圍行為和一致性,它們比alloca()好得多。 正如我所看到的, VLA是一種alloca()製造的。

當然,使用所需空間的majorant的本地結構或數組仍然更好,如果您沒有使用普通malloc()這樣的majorant堆分配可能理智。 我沒有看到真正需要alloca()VLA的理智用例


The alloca function is great and and all the naysayers are simply spreading FUD.

void foo()
{
    int x = 50000; 
    char array[x];
    char *parray = (char *)alloca(x);
}

Array and parray are EXACTLY the same with EXACTLY the same risks. Saying one is better than another is a syntactic choice, not a technical one.

As for choosing stack variables vs heap variables, there are a LOT of advantages to long running programs using stack over heap for variables with in-scope lifetimes. You avoid heap fragmentation and you can avoid growing your process space with unused (unusable) heap space. You don't need to clean it up. You can control the stack allocation on the process.

Why is this bad?


One pitfall with alloca is that longjmp rewinds it.

That is to say, if you save a context with setjmp , then alloca some memory, then longjmp to the context, you may lose the alloca memory (without any sort of notice). The stack pointer is back where it was and so the memory is no longer reserved; if you call a function or do another alloca , you will clobber the original alloca .

Incidentally, this provides a plausible mechanism for deliberately freeing memory that was allocated with alloca .

This isn't documented anywhere; of course the ISO C description of setjmp will say nothing about interactions with alloca , since it describes no such function. Implementations that provide alloca tend not to document this either.

The focus is usually on the concept that alloca memory is associated with a function activation, not with any block; that multiple invocations of alloca just grab more stack memory which is all released when the function terminates. Not so; the memory is actually associated with the procedure context. When the context is restored with longjmp , so is the prior alloca state.

I have run into this which is how I know.


老問題,但沒有人提到它應該被可變長度數組取代。

char arr[size];

代替

char *arr=alloca(size);

它在標準的C99中,在許多編譯器中作為編譯器擴展存在。


Not very pretty, but if performance really matter, you could preallocate some space on the stack.

If you already now the max size of the memory block your need and you want to keep overflow checks, you could do something like :

void f()
{
    char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
    SomeType *p = (SomeType *)array;

    (...)
}




alloca