2013-06-18 103 views
7

我正在閱讀K & R中的數組算術部分,並且發現了一些好奇的東西。我發佈了整個段落的背景,但我主要集中在大膽的部分。用於數組的C指針算術

如果p和q指向同一陣列的成員,然後像==, !=,<關係,> =,等等,正常工作。例如,p < q如果p指向 而不是q的數組的早期成員,則q爲真。任何指針可以是 有意義地與零相等或不平等進行比較。但 行爲未定義算術或與指針 不指向同一數組的成員的指針。 (有一個例外: 過去的陣列的端部的第一個元素的地址可在指針運算使用 。)

什麼是該異常的原因是什麼?在定義大小時,是否將多餘的內存分配給任何數組的末尾?如果是這樣,爲了什麼目的?它是用空字符結束數組嗎?

+1

理解這個最簡單的方法是:a [b] = a + b。看第98頁的解釋 – SheetJS

+2

@Nirk:嗯?這與這有什麼關係? – jason

回答

8

的原因是這樣你就可以在這樣的循環遞增指針:

char a[42], *p; 

for (p = a; p < &a[sizeof a]; p++) // or p != &a[sizeof a] 
{ 
    /* ... */ 
} 

如果沒有額外的規則,這將是不確定的行爲,因爲該指針將是無效的。

0

在數組末尾沒有分配額外的內存。它只是說你可以在指針算術中使用'End'標記的地址。開始指向數組的第一個元素。終點指向的第一個元素過去數組的末尾。

----------------- 
| | | | | 
----------------- 
^    ^
Begin   End 
4

是被定義它們的大小當分配給任何陣列的端部的多餘碎片存儲器?

不是。您引用的上下文很重要。你粗體的例外是參考指針算術(和關係)。這是說,如果你做指針之間的指針關係而不是指向同一個數組的成員,那麼你得到udb。但是,有一個例外,就是如果指針中的任何一個都指向數組末尾的第一個元素。

如果是這樣,爲了什麼目的?

null答案,因爲它假設一個錯誤的前提。

是否以空字符結束數組?

這樣做的原因是,使得相比於數組的末尾是合法的號,也就是比較來&a[sizeof a]a是一個數組。請注意,&a[sizeof a]是數組末尾的第一個元素。如果p是指向a元素的指針或者也是數組末尾之後的第一個元素,則可以將p&a[sizeof a]進行比較。

我引自C99 specification,第6.5.8.5節。

當比較兩個指針時,結果取決於指向對象的地址空間中的相對位置。如果指向對象或不完整類型的兩個指針都指向同一個對象,或者兩個指向同一個數組對象的最後一個元素,則它們相等。如果指向的對象是同一個聚合對象的成員,那麼稍後聲明的結構成員的指針比結構中較早聲明的成員的指針要多,指向具有較大下標值的數組元素的指針比指向同一數組元素的指針大具有較低的下標值。如果表達式P指向數組對象的元素,並且表達式Q指向同一數組對象的最後一個元素,則指針表達式Q + 1的比較結果大於P。在所有其他情況下,行爲是不確定的。

0

您只需計算一個數組尾部的對象的地址,並承諾您不會因此而陷入困境。您不允許取消引用該指針。

這個承諾很重要的一個例子是,一個對象可能被分配在內存的最後,所以當你計算出地址時,一個結束的地址會導致算術溢出。如果要迭代指針通過該數組,則在最後一次迭代之後,算術溢出可能會導致指針環繞並指向NULL。

這可能會導致比較結果被倒置,並且它可能會使用數組邊界檢查器跳動各種警報鈴聲,或者如果CPU使用例如飽和算法,它可能會簡單地計算錯誤的地址。

因此,編譯器和鏈接器有責任確保不會發生這種情況,程序員有責任確保編譯器和鏈接器將責任限制在一個簡單的情況下,當你在末尾運行n元素時,你必須堅持相同的保證。