2013-05-12 35 views
7

指針假設我有for循環用來存儲使用指針陣列中的零點是這樣的:測試使用關係運算符在for循環

int *vp, values[5]; 

for(vp = &values[5-1]; vp >= &values[0]; vp--) 
    *vp = 0; 

第C的書本 - 指針表示,有一個問題與此循環,因爲比較vp >= &values[0]是不確定的,因爲它移動到數組邊界之外。但是如何?

+2

它的「不確定」,但系統95%會被罰款。如果'&values [0]'== 0x0,比較將失敗的* only *方案。 – 2013-05-12 16:43:18

+0

可能超過95%。 =) – 2013-05-12 16:44:46

+0

我敢打賭它不適用於Symbolics Lisp機器上的C實現。不過,我不知道是否還有其他的使用方法;該公司近20年前就停業了。 – Barmar 2013-05-12 16:48:39

回答

2

假設一個指針相當於一個無符號整數,我們可以看到,只有當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),和它會導致無限循環(假設所有內存都是可寫的)或分段錯誤。

根據標準,它也是未定義的行爲(因爲您可以在數組之後處理一個元素,但不在其之前),但實際上,在任何現實的系統上都不會失敗。

+0

所以,使用這個是安全的,或者我可以檢查大於只。 – 2013-05-12 16:49:16

+0

@ ashish2expert是的,這是我的評估,它是安全的,除非你正在處理一個非常奇怪的系統(你可能不在這裏)。 – 2013-05-12 16:50:38

+3

此代碼可能會在執行段與偏移量指針時失敗,但也可能失敗,因爲優化器有權假定'vp> =&values [0]'從未執行,而'vp'不會指向該數組或超出它的數組(因爲這樣做會是未定義的行爲)。因此,優化器可能表現得好像這個表達式總是爲真,並可能消除它,導致無限循環。 – 2013-05-12 17:01:32

4

即使排除舊的或異國情調的處理器體系結構,此代碼也不安全。

編譯器中的優化器嵌入了許多關於該語言的規則。當它看到vp >= &values[0](其中values是一個數組)時,優化程序有權假定vp指向數組元素或超出數組的元素,因爲否則該表達式不是由C語言定義的。

嵌入優化器的規則和機制因此可能決定vp >= &values[0]始終爲真,因此它可能會產生代碼,就好像for (vp = &values[5-1]; ; vp--)已被寫入。如果*vp = 0使用指向數組外部的vp進行評估,則會導致沒有終止條件的循環以及未定義的行爲。

+0

是的,這是可能的,但它可能不太可能,除非你有一些優化標誌設置。如果我沒有記錯,GCC有一個選項可以打破這種循環,但默認情況下它是禁用的,Clang優化器甚至不會觸及那樣的東西。 – 2013-05-12 17:10:28

+0

@ RichardJ.RossIII:你認爲這個代碼是否符合C標準是安全的,或者如果你對某些編譯器有特定的知識,那麼就是安全的?請引用支持此的文檔。 – 2013-05-12 17:13:33

0
int values[5]; 
size_t idx; 

for(idx=5; idx-- > 0;) { 
    values[idx] = 0; 
    } 

這看起來可能很奇怪,但一旦你習慣了它,它可能是一個很好的模式。

  • 您避免(可能)的指針值下溢(這是嚴格未定義)
  • 不能下溢,無符號的類型(如爲size_t)將環繞,這肯定會導致非法訪問,這是喜歡偷偷摸摸一個地址,這將損壞內存,而不是快速失敗
  • WRT轉速/性能:從編譯器的角度來看,指針和索引基本相同。編譯器甚至可以爲這兩種情況生成相同的代碼。

順便說一句:如果你堅持指針的使用,下面會做:

int *vp, values[5]; 

for(vp = &values[5-1]; vp ; vp = (vp > &values[0]) ? vp-1 : NULL;) { 
    *vp = 0; 
    }