1. 程式人生 > >陣列的下標越界與記憶體溢位

陣列的下標越界與記憶體溢位

很相似的兩個概念,一不小心就會混淆

首先,對兩個名詞做一個大概的解釋:

  1. 下標越界
    引用陣列元素時,使用的下標超過了該陣列下標的應有範圍,但應注意的是:
    C/C++不對陣列做邊界檢查。 可以重寫陣列的每一端,並寫入一些其他變數的陣列或者甚至是寫入程式的程式碼。不檢查下標是否越界可以有效提高程式執行的效率,因為如果你檢查,那麼編譯器必須在生成的目的碼中加入額外的程式碼用於程式執行時檢測下標是否越界,這就會導致程式的執行速度下降,所以為了程式的執行效率,C / C++才不檢查下標是否越界。發現如果陣列下標越界了,那麼它會自動接著那塊記憶體往後寫。
    關於C/C++為什麼不對陣列的下標是否越界做檢查,可以參考:
    http://www.xuebuyuan.com/967089.html

    因為編譯器不會自動檢測你的陣列下標是否越界,而是把這個任務交給了程式設計師自己,所以我們在寫程式,引用陣列元素時,一定注意不要讓陣列的下標越界。
    還有,初學者一定不能忘了陣列的下標是從0開始的,不是常識中的從1開始。
  2. 記憶體溢位
    初始化陣列(給陣列元素賦值)時,初始化(賦值)元素的個數超過了陣列定義時元素的個數。這裡的元素個數就是在定義陣列時那個方框框裡的數字,對於多維陣列來說,元素個數 = 每個方框框裡的數字之積。
    當然,求陣列元素個數可以用公式:
    陣列元素個數 = sizeof(陣列名)/sizeof(陣列任意一個元素)

用兩個具體的例子來看看它們之間的區別:

1、 下標越界

#include <stdio.h>

int main()
{
    int i=10, arr[10];
    int sz = sizeof(arr) / sizeof(arr[0]);
    int index = 0;
    scanf("%d", &index);
    arr[index] = 20;
    printf("arr[%d] = %d\n", index,arr[index]);
    system("pause");
    return 0;
}

陣列定義為arr[10],它的下標範圍是 0–9 ,超出這個範圍就會發生下標溢位
這裡寫圖片描述

這裡寫圖片描述
以上兩張圖片是在VS2013裡面的執行結果,兩次輸入的下標都會越界,但為什麼第一張圖報錯了(陣列下標越界),而第二張沒有。
這是因為VS2013這個編譯器在處理陣列下標時,只認為當引用的下標等於陣列元素個數時下標越界(可能是怕初學者把陣列下標當成是以0開始的吧),別的情況別不回去檢測和處理(當我輸入的值大於等於11,程式都不會報錯)。

這裡寫圖片描述

這裡寫圖片描述
以上是在VC6.0的執行結果,VS2013不同,VC6.0裡報錯出現在輸入值為11時(當輸入12時也會報錯),而輸入10時並不會報錯。
這是因為VC6.0裡認為當引用陣列元素時,若陣列的下標比陣列的元素個數大 1 (或大 2)時下標越界,。而對於其餘的情況不予檢測。

這裡寫圖片描述

而在Linux(CentOS6.5)裡面執行時,就是我們開頭說的那種下標越界的情況,不管你下標咋越界,我的編譯器都不會對陣列下標做邊界檢查。

2、 記憶體溢位

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>          

int main()
{
    int count[100], i;
    for (i = 0; i <= 100; i++)
    {
        count[i] = 1;
    }
    system("pause");
    return 0;
}

陣列定義為count[100],則陣列共有100個元素。這裡初始化時,for迴圈一共迴圈了101次,所以是要給陣列初始化101個元素的值,而陣列只有100個元素,這樣就導致了記憶體溢位。

這裡寫圖片描述

VS2013環境下,編譯連結執行都可以,但會丟擲一個錯誤—>>陣列記憶體溢位。

這裡寫圖片描述

這是在VC6.0環境下,編譯連結執行都可以,但同樣會出錯。

這裡寫圖片描述

這是在Linux(CentOS6.5)環境下,編譯連結執行都可以,但程式停不下來,關閉終端時可以看到程式仍在進行。

因此,雖然陣列在C語言中是個很強大的東西,但在應用時一定要注意,千萬不要出現下標越界的情況,因為這樣會造成不可想象的錯誤。比如下標越界那個例子中的 i 我定義為10,當在VS2013環境下,我輸入的下標值為12,在輸出arr[12] = 20 的同時,會把i的值也改為20(VS中定義的兩個變數,分配記憶體時會在兩個變數的記憶體空間之間隔出兩個空間,空間大小為整形大小),這樣別處再用到 i 時 會得到一個錯誤的 i 的值,可能機會導致一連串的錯誤,致使結果與預期相差甚遠。同時,在初始化陣列時,要注意不要初始化的元素個數,不要超出了定義時的個數。