2013-02-01 39 views
0

在下面的代碼中,我嘗試訪問數組的'-1'元素,但沒有得到任何運行時錯誤。當我訪問數組的超出界限元素時,爲什麼不會出現運行時錯誤?

#include <stdio.h> 

int A[10] = {0}; 

int main(){ 

    A[-1] += 12; 

    printf("%d",A[-1]); 

    return 0; 
} 

當運行的代碼,它輸出12這意味着它是加入12向不存在的A [-1]。直到今天,每當我試圖訪問一個超出界限的元素,我都有一個運行時錯誤。我以前從來沒有嘗試過使用簡單的代碼。

任何人都可以解釋爲什麼我的代碼運行成功?

我把它運行在我的電腦上,也在ideone上運行,在兩種情況下都成功運行。

+0

未定義的行爲。如果'A'是一個指向第一個元素的指針,那麼你可以這樣做,但這更多的是一個負面的索引,而不是一個超出界限的東西。 – chris

+0

我也嘗試訪問A [11],並且它成功運行。 – 2147483647

+3

@ A.06:是的。 「未定義的行爲」包括「它已成功運行」。未定義的行爲還包括「可怕的崩潰」和「獨角獸從您的電腦屏幕飛出」。它*字面意思*表示你不能以任何形式的可靠性來推理它的行爲,這就是爲什麼UB正是你不想做的。 –

回答

1

你會看到,當你像這樣分配一個變量時,它會着陸在堆棧上。 Stack在你調用的每個函數中保存有關局部變量的小包信息,用簡單的話來說。運行時可以檢查是否超出了分配堆棧的界限,但是如果在堆棧上的無效位置寫入一些數據,則無法檢查。堆可以如下所示:

[4個字節 - 一些PTR] [4個字節 - A的第一個元素] [4個字節 - A的第二個元素] ...

當你試圖給-1th數組的元素,你實際上試圖讀取數組之前的四個字節(四個字節,因爲它是一個int數組)。您可以覆蓋堆棧中保存的一些數據 - 但仍然存在於有效進程的內存中,因此係統沒有任何投訴。

嘗試在Visual Studio中運行在釋放模式驗證碼:

#include <stdio.h> 

int main(int argc, char * argv[]) 
{ 
    // NEVER DO IT ON PURPOSE! 
    int i = 0; 
    int A[5]; 

    A[-1] = 42; 
    printf("%d\n", i); 

    getchar(); 
    return 0; 
} 

編輯:響應意見。

我錯過了這個事實,即A是全球性的。它不會被保存在堆棧中,而是在二進制模塊的.data段中(大多數情況下),但其餘的解釋如下:A [-1]仍在進程內存中,因此賦值不會引發AV。但是,這樣的賦值會覆蓋某些東西,即A之前(可能是指針或二進制模塊的其他部分),導致未定義的行爲。

請注意,根據編譯器(或編譯器模式),我的示例可能工作,也可能不工作。例如,在調試模式下,程序返回0--我想,內存管理器會在堆棧幀之間插入一些哨兵數據來捕獲緩衝區溢出/溢出等錯誤。

+3

除了definiton int A [10] = {0};'(在OP的代碼中)在任何函數的主體之外,所以我懷疑'A'是在堆棧上創建的那種情況:) –

+0

啊,你是對的。不過,我猜想,這仍然是編寫進程內存的問題,是什麼讓這段代碼不會崩潰。 – Spook

+0

@Spook,這是一個合理的解釋。只是不要指望這是原因。它可能只是編譯器處於一個棘手的心情:) – chris

1

C和C++沒有任何邊界檢查。它是語言的一部分。這是爲了使語言更快地執行。

如果你想邊界檢查使用另一種語言。 Java也許?

隨着您的代碼執行,您只是幸運。

+0

如果你想邊界檢查,使用適當的容器和'at()'。 – chris

+0

但是,它們在A [-1]不存在時如何將12添加到A [-1]? – 2147483647

+0

@ A.06,它仍然存在於內存中,但試圖推測未定義的行爲是多麼毫無意義。 – chris

1

在C++(和C)中,數組不檢出範圍索引。他們不是班級。

在C++ 11,但是你可以使用std::array<int,10>at()功能:

std::array<int,10> arr; 

arr.at(-1) = 100; //it throws std::out_of_range exception 

或者你可以使用std::vector<int>at()成員函數。

相關問題