回覆列表
  • 1 # 大寶8211

    顧名思義,stack overflow 就是是棧溢位了。在進行數值運算時,我們常常要和運算結果的溢位打交道。數值運算結果可能上溢(overflow),也可能是下溢(underflow)。不過棧的溢位顯然只可能是上溢,即棧空間被用完了。在提起“棧”(stack)這個概念的時候,千萬不要忘記了它的兄弟“堆”(heap),也要切記不要把二者搞混了。

    那麼,什麼時候會把給用完了呢?如果我們記得C程式中的區域性變數是在棧中分配的,函式呼叫會佔用一部分棧空間,則可以很容易地構造出相應的測試用例。

    1、定義佔用空間過大的區域性變數所導致的棧溢位

    C:\> more stack_local.c

    /*

    * Allocate too much memory from stack will cause stack overflow.

    */

    #include <stdio.h>

    int main(int argc, char *argv[])

    {

    int foo[1000000];

    return 0;

    }

    C:\> cl stack_local.c

    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86

    Copyright (C) Microsoft Corporation. All rights reserved.

    stack_local.c

    Microsoft (R) Incremental Linker Version 8.00.50727.42

    Copyright (C) Microsoft Corporation. All rights reserved.

    /out:stack_local.exe

    stack_local.obj

    C:\> stack_local

    此時出現一個異常對話方塊:stack-local.jpg 。

    2、函式遞迴呼叫導致的棧溢位

    C:\> more stack_recursive.c

    /*

    * Infinite recursive calls will lead to stack overflow soon.

    */

    #include <stdio.h>

    static void foo(void);

    static void bar(void);

    int main(int argc, char *argv[])

    {

    foo();

    return 0;

    }

    static void foo(void)

    {

    bar();

    }

    static void bar(void)

    {

    foo();

    }

    C:\> cl stack_recursive.c

    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86

    Copyright (C) Microsoft Corporation. All rights reserved.

    stack_recursive.c

    Microsoft (R) Incremental Linker Version 8.00.50727.42

    Copyright (C) Microsoft Corporation. All rights reserved.

    /out:stack_recursive.exe

    stack_recursive.obj

    C:\> stack_recursive

    該程式沒聲沒息就結束了。檢視程序返回值能發現它其實是異常終止了。只不過沒有像 stack_local 那樣彈出一個對話方塊。

    C:\> echo %errorlevel%

    -1073741819

    要搞清楚這兩個程式為什麼有這點細微的區別,可以查閱一下二者的彙編程式碼。原來是 _chkstk() 在起作用,其中 stack_local 在程式初始載入時就會導致 _chkstk() 失敗,觸發異常。而 stack_recursive 可以正確載入,並執行一段時間,然後導致棧溢位,並觸發異常。

    要正確處理棧溢位採用以下辦法:

    (1)修正我們的程式,不要造成無窮遞迴或太深的遞迴。我們可以把某些遞迴程式碼非遞迴化,例如那個經典的 qsort ,最好就用非遞迴的演算法來實現,就比較皮實一點。

    (2)修正我們的程式,不要定義過大的區域性變數,特別是在定義大結構、大陣列時要格外小心。有時我們可能會用 _alloca() 這樣的特殊函式直接在棧上分配空間,更要多加註意。

    (3)利用編譯器的特性,將程序允許的棧大小設定得大一些。例如可以採用 MSC 中的 /STACK 引數開關。

    (4)對於那些還可能導致棧溢位的程式碼,採用 Microsoft 的結構化異常處理或標準的 C++ 異常處理機制,結合 _resetstkoflw() 進行處理。當然了,要是不嫌麻煩,我們也可以自己探測所用棧的大小,動態地檢測是否可能導致棧溢位,以避免可能的異常。

  • 中秋節和大豐收的關聯?
  • 物流系統的地位和作用?