指針假設我有for循環用來存儲使用指針陣列中的零點是這樣的:測試使用關係運算符在for循環
int *vp, values[5];
for(vp = &values[5-1]; vp >= &values[0]; vp--)
*vp = 0;
第C的書本 - 指針表示,有一個問題與此循環,因爲比較vp >= &values[0]
是不確定的,因爲它移動到數組邊界之外。但是如何?
指針假設我有for循環用來存儲使用指針陣列中的零點是這樣的:測試使用關係運算符在for循環
int *vp, values[5];
for(vp = &values[5-1]; vp >= &values[0]; vp--)
*vp = 0;
第C的書本 - 指針表示,有一個問題與此循環,因爲比較vp >= &values[0]
是不確定的,因爲它移動到數組邊界之外。但是如何?
假設一個指針相當於一個無符號整數,我們可以看到,只有當values
從地址0開始時,問題纔會存在,在這種情況下指針會在遞減後迴繞併成爲UINT_MAX
。
爲了形象化的問題,讓我們通過會發生什麼步驟,假設在地址0x0 values
開始:
iteration 1:
vp = 0x4, *vp = 0;
iteration 2:
vp = 0x3, *vp = 0;
iteration 3:
vp = 0x2, *vp = 0;
iteration 4:
vp = 0x1, *vp = 0;
iteration 5:
vp = 0x0, *vp = 0;
iteration 6:
vp = 0xFFFFFFFF; *vp = ?? // uh oh!
因此,vp
絕不會小於指針的最低值(0),和它會導致無限循環(假設所有內存都是可寫的)或分段錯誤。
根據標準,它也是未定義的行爲(因爲您可以在數組之後處理一個元素,但不在其之前),但實際上,在任何現實的系統上都不會失敗。
所以,使用這個是安全的,或者我可以檢查大於只。 – 2013-05-12 16:49:16
@ ashish2expert是的,這是我的評估,它是安全的,除非你正在處理一個非常奇怪的系統(你可能不在這裏)。 – 2013-05-12 16:50:38
此代碼可能會在執行段與偏移量指針時失敗,但也可能失敗,因爲優化器有權假定'vp> =&values [0]'從未執行,而'vp'不會指向該數組或超出它的數組(因爲這樣做會是未定義的行爲)。因此,優化器可能表現得好像這個表達式總是爲真,並可能消除它,導致無限循環。 – 2013-05-12 17:01:32
即使排除舊的或異國情調的處理器體系結構,此代碼也不安全。
編譯器中的優化器嵌入了許多關於該語言的規則。當它看到vp >= &values[0]
(其中values
是一個數組)時,優化程序有權假定vp
指向數組元素或超出數組的元素,因爲否則該表達式不是由C語言定義的。
嵌入優化器的規則和機制因此可能決定vp >= &values[0]
始終爲真,因此它可能會產生代碼,就好像for (vp = &values[5-1]; ; vp--)
已被寫入。如果*vp = 0
使用指向數組外部的vp
進行評估,則會導致沒有終止條件的循環以及未定義的行爲。
是的,這是可能的,但它可能不太可能,除非你有一些優化標誌設置。如果我沒有記錯,GCC有一個選項可以打破這種循環,但默認情況下它是禁用的,Clang優化器甚至不會觸及那樣的東西。 – 2013-05-12 17:10:28
@ RichardJ.RossIII:你認爲這個代碼是否符合C標準是安全的,或者如果你對某些編譯器有特定的知識,那麼就是安全的?請引用支持此的文檔。 – 2013-05-12 17:13:33
int values[5];
size_t idx;
for(idx=5; idx-- > 0;) {
values[idx] = 0;
}
這看起來可能很奇怪,但一旦你習慣了它,它可能是一個很好的模式。
順便說一句:如果你堅持指針的使用,下面會做:
int *vp, values[5];
for(vp = &values[5-1]; vp ; vp = (vp > &values[0]) ? vp-1 : NULL;) {
*vp = 0;
}
它的「不確定」,但系統95%會被罰款。如果'&values [0]'== 0x0,比較將失敗的* only *方案。 – 2013-05-12 16:43:18
可能超過95%。 =) – 2013-05-12 16:44:46
我敢打賭它不適用於Symbolics Lisp機器上的C實現。不過,我不知道是否還有其他的使用方法;該公司近20年前就停業了。 – Barmar 2013-05-12 16:48:39