3 回答

TA貢獻1900條經驗 獲得超5個贊
您說對了:這是未定義的行為,您不能指望它總是在產生0
。
至于為什么在這種情況下看到零的原因:現代操作系統(tǒng)將內存分配給進程中相對較粗的塊(稱為頁面),這些塊比單個變量大得多(在x86上至少為4KB)。當您有一個全局變量時,它將位于頁面上的某個位置。假設a
類型為,int[][]
并且int
s為系統(tǒng)上的四個字節(jié),a[27][27]
則從的開頭開始約500個字節(jié)a
。因此,只要a
在頁面的開頭附近,訪問a[27][27]
就會被實際的內存支持,并且讀取它不會導致頁面錯誤/訪問沖突。
當然,您不能指望這一點。例如,如果a
前面有將近4KB的其他全局變量,那么a[27][27]
它將不由內存支持,并且當您嘗試讀取它時,過程將崩潰。
即使該過程沒有崩潰,您也不能指望獲得該值0
。如果您在現代的多用戶操作系統(tǒng)上有一個非常簡單的程序,除了分配該變量并打印該值之外什么也不做,您可能會看到0
。在將內存移交給進程時,操作系統(tǒng)會將內存內容設置為某個良性值(通常為全零),以使來自一個進程或用戶的敏感數據不會泄漏到另一個進程或用戶。
但是,不能完全保證讀取的任意內存為零。您可以在未分配內存的情況下在平臺上運行程序,并且會看到從上一次使用開始碰到的任何值。
同樣,如果a
后面跟隨著足夠多的初始化為非零值的其他全局變量,則訪問a[27][27]
將向您顯示恰好在那里的任何值。

TA貢獻1859條經驗 獲得超6個贊
訪問數組越界是未定義的行為,這意味著結果是不可預知的,從而該結果a[27][27]存在0是不可靠的。
clang如果我們使用,可以很清楚地告訴你-fsanitize=undefined:
runtime error: index 27 out of bounds for type 'int [4][4]'
一旦你不確定的行為,編譯器真的可以做任何事情,我們甚至還看到其中的例子gcc已經翻了有限循環(huán)進入一個無限循環(huán)基于圍繞未定義行為的優(yōu)化。雙方clang并gcc在某些情況下可以產生和未定義的指令操作碼,如果它檢測未定義的行為。
為什么它是未定義的行為,為什么越界指針算術是未定義的行為?提供了很好的原因總結。例如,結果指針可能不是有效地址,該指針現在可以指向分配的內存頁面之外,您可以使用內存映射的硬件而不是RAM等。
存儲靜態(tài)變量的段很可能比您正在分配的數組或要踩的段大得多,盡管剛好被清零,所以在這種情況下您很幸運,但行為又完全不可靠。您的頁面大小很可能是4k,對的訪問a[27][27]在該范圍內,這可能就是為什么您沒有看到分段錯誤的原因。
標準怎么說
在C99標準草案告訴我們這是第不確定的行為6.5.6 加法運算符覆蓋指針算法是一個數組訪問來自哪個下來。它說:
將具有整數類型的表達式添加到指針或從指針中減去時,結果將具有指針操作數的類型。如果指針操作數指向數組對象的元素,并且數組足夠大,則結果指向與原始元素偏移的元素,以使結果數組元素和原始數組元素的下標之差等于整數表達式。
[...]
如果指針操作數和結果都指向同一數組對象的元素,或者指向數組對象的最后一個元素,則求值不會產生溢出;否則,行為是不確定的。如果結果指向數組對象的最后一個元素之后,則不應將其用作被評估的一元*運算符的操作數。
未定義行為的標準定義告訴我們,該標準對行為沒有任何要求,并指出可能的行為是不可預測的:
在使用非便攜式或錯誤程序構造或錯誤數據時的行為,對此國際標準不施加任何要求
注意可能的不確定行為范圍包括完全忽略情況并產生不可預測的結果,[...]

TA貢獻1757條經驗 獲得超8個贊
這是標準的引號,它指定什么是未定義的行為。
J.2不確定行為
即使顯然可以使用給定下標訪問對象,數組下標也超出范圍(如在聲明為int a [4] [5]的左值表達式a [1] [7]中)(6.5.6)。
將指針加或減到數組對象或整數類型中或超出數組對象和整數類型會產生指向數組對象正好超出數組對象的結果,并用作評估的一元*運算符的操作數(6.5.6)。
在您的情況下,數組下標完全在數組之外。完全依賴于該值將為零是不可靠的。
此外,整個程序的行為是有問題的。
- 3 回答
- 0 關注
- 497 瀏覽
添加回答
舉報