2011-03-17 55 views
6

我正在瀏覽一個有一些c FAQ的webpage,我發現了這個說法。c和數組邊界中的指針算術

類似地,如果有10種元素和ip 指向[3],你不能計算或 接入IP + 10或IP - 5.(有 一種特殊情況:在能,在這種 情況下,計算,但不連接, 指針不存在的元素 剛好超出數組的末尾, 在這種情況下是&一個[10]。

我感到困惑由聲明

你不能計算IP + 10

我能理解訪問元素出界是不確定的,但計算!!!

我寫了下面的代碼片段其計算(讓我知道這是什麼網站計算的意思)的指針出界外。

#include <stdio.h>                                         

int main()                                           
{                                             
     int a[10], i;                                        
     int *p;                                          

     for (i = 0; i<10; i++)                                      
       a[i] = i;                                       

     p = &a[3];                                         

     printf("p = %p and p+10 = %p\n", p, p+10);                                 
     return 0;                                         
}      

$ ./a.out                                  
p = 0xbfa53bbc and p+10 = 0xbfa53be4  

我們可以看到p + 10指向過去p的10個元素(40個字節)。那麼網頁中的聲明究竟意味着什麼。我誤解了一些東西嗎?

即使以K & R(A.7.7)該語句是由:

+運算的結果是操作數的 總和。可以添加指向數組中的 對象的指針和任何 整數類型的值。 ... 總和是與原始指針 相同類型的指針,並且指向 同一數組中的另一個對象 與原始對象 適當偏移。因此,如果P是指向數組中的對象的指針,則表達式P + 1 是指向 陣列中的下一個對象的指針。 如果總和指針指向 超出數組範圍, 除了第一個位置超出 高端,結果是 未定義。

什麼是「未定義」的意思。這是否意味着總和將是未定義的,還是僅僅意味着當我們取消引用時,行爲是未定義的。即使我們沒有對它進行解引用並且只是計算指向元素出界的指針,該操作也是未定義的。

+0

這意味着此特定位置的內容未定義(隨機)。 – bioffe 2011-03-17 16:01:50

+2

未定義表示獲得您所期望的與將銀行帳戶中的錢轉入我的銀行帳戶一樣糟糕。繼續編碼未定義的結構...總有一天會發生意想不到的事情......我將帶着我的夢想假期:) – pmg 2011-03-17 16:12:58

回答

9

未定義行爲意味着正是:絕對什麼事情都可能發生。它可以靜靜地成功,它可以默默地失敗,它可能會導致你的程序崩潰,它可能會藍屏你的操作系統,或者它可能會抹去你的硬盤。其中一些不太可能,但就C語言標準而言,它們都是允許的行爲

在這種特殊情況下,是的,C標準是說即使計算的地址有效數組邊界以外的指針,而不取消引用它是未定義的行爲。之所以這樣說,是因爲有一些神祕的系統在進行這樣的計算會導致某種錯誤。例如,您可能在可尋址內存的最後有一個數組,並且構造一個超出該數組的指針會導致特殊地址寄存器中的溢出,從而產生陷阱或錯誤。 C標準希望允許這種行爲,以便儘可能便攜。

但實際上,您會發現構建這樣一個無效地址而不取消引用它的行爲在絕大多數常見使用的系統上都有明確定義的行爲。創建一個無效的內存地址將不會產生不良影響,除非您嘗試對其進行解除引用。但是,當然,最好避免創建這些無效地址,以便即使在那些神祕的系統上,您的代碼也能夠完美工作。

+0

謝謝亞當。這意味着在我的系統上這是一個「已定義」的行爲。但是如果在同一個系統上,陣列在可尋址內存的末尾,這可能會造成麻煩。而在其他系統上,可能會有某種「指針驗證硬件」,它根本不允許進行這種操作,即使算術也不算解引用它。我正確地得到了嗎?再一次感謝你。 – jailed 2011-03-17 16:30:16

+0

@jailed它甚至可能不是您的系統上定義的行爲;您需要檢查您正在使用的特定C編譯器的文檔。 – Jonathan 2011-03-17 16:43:42

+0

「_創建一個無效的內存地址將沒有不良影響_」,除非您使用無效的段標識符 – curiousguy 2011-12-02 10:29:39

4

該網頁的措辭令人困惑,但技術上正確。 C99 language specification (section 6.5.6)討論了加法表達式,包括指針運算。子項目8特別指出,計算一個超過數組末尾的指針不會導致溢出,但除此之外,行爲是不確定的。從更實際的角度來看,C編譯器通常會讓你避開它,但是你對結果值的處理是由你決定的。如果嘗試將生成的指針取消引用爲某個值(如K & R狀態),則行爲未定義。

在編程術語中,未定義的意思是「不這樣做」。基本上,它意味着定義語言如何工作的規範並沒有在這種情況下定義適當的行爲。因此,理論上任何事情都可能發生。通常發生的一切是你的程序中有一個沉默或嘈雜的(段錯誤)錯誤,但許多程序員喜歡對導致未定義行爲的其他可能結果進行開玩笑,比如刪除所有文件。

+0

不,您*無法*計算出這個值。就像C標準所說的,即使只是計算出界限指針也是未定義的行爲。 – 2011-03-17 16:10:58

+1

@Adam我想我混淆未定義與通常實施。事實上,任何C編譯器都會爲您做數學運算併爲您提供一個值,並將該值的取消引用保留爲未定義。 – Jonathan 2011-03-17 16:24:31

+0

@亞當,你是對的。 C99規範(http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf)第6.5.6節第8項明確規定,使用指針算術計算值爲1過去數組的末尾不應導致溢出,但指向更遠是未定義的。我會相應地更新我的答案。 – Jonathan 2011-03-17 16:39:18

2

行爲將在下面的情況是不確定的

int a[3]; 
(a + 10) ; // this is UB too as you are computing &a[10] 
*(a+10) = 10; // Ewwww!!!!