簡體   English   中英

是對空指針未定義行為執行算術?

[英]Is performing arithmetic on a null pointer undefined behavior?

它看起來像下面的程序計算一個無效的指針,因為除了賦值和比較的平等之外, NULL不是什么好事:

#include <stdlib.h>
#include <stdio.h>

int main() {

  char *c = NULL;
  c--;

  printf("c: %p\n", c);

  return 0;
}

然而,似乎GCC或Clang針對未定義行為的警告或工具都沒有說這實際上是UB。 這個算術實際上是否有效,而且我太迂腐了,或者這是我們應該報告的檢查機制的缺陷嗎?

測試:

$ clang-3.3 -Weverything -g -O0 -fsanitize=undefined -fsanitize=null -fsanitize=address offsetnull.c -o offsetnull
$ ./offsetnull
c: 0xffffffffffffffff

$ gcc-4.8 -g -O0 -fsanitize=address offsetnull.c -o offsetnull
$ ./offsetnull 
c: 0xffffffffffffffff

似乎很好地記錄了Clang和GCC使用的AddressSanitizer更側重於解除壞指針的引用,所以這很公平。 但其他檢查也沒有抓住它: - /

編輯 :我問這個問題的部分原因是-fsanitize標志能夠動態檢查生成的代碼中的良好定義。 這是他們應該抓住的東西嗎?

指向不指向數組的指針的指針算法是未定義的行為。
此外,取消引用NULL指針是未定義的行為。

char *c = NULL;
c--;

是未定義的已定義行為,因為c未指向數組。

C ++ 11標准5.7.5:

當向指針添加或從指針中減去具有整數類型的表達式時,結果具有指針操作數的類型。 如果指針操作數指向數組對象的元素,並且數組足夠大,則結果指向偏離原始元素的元素,使得結果元素和原始數組元素的下標的差異等於整數表達式。 換句話說,如果表達式P指向數組對象的第i個元素,則表達式(P)+ N(等效地,N +(P))和(P)-N(其中N具有值n)指向分別為數組對象的第i + n和第i - 第n個元素,只要它們存在。 此外,如果表達式P指向數組對象的最后一個元素,則表達式(P)+1指向一個超過數組對象的最后一個元素,如果表達式Q指向一個超過數組對象的最后一個元素,表達式(Q)-1指向數組對象的最后一個元素。 如果指針操作數和結果都指向同一個數組對象的元素,或者指向數組對象的最后一個元素,則評估不應產生溢出; 否則,行為未定義。

是的,這是未定義的行為,是-fsanitize=undefined應該捕獲的東西; 它已經在我的TODO列表上添加了一個檢查。

FWIW,這里的C和C ++規則略有不同:向空指針添加0並從另一個中減去一個空指針在C中有未定義的行為但在C ++中沒有。 對空指針的所有其他算術在兩種語言中都有未定義的行為。

不僅禁止對空指針進行算術運算,而且陷阱嘗試取消引用的實現失敗也會對空指針進行陷阱運算,這極大地降低了空指針陷阱的好處。

標准中沒有定義任何情況,其中向空指針添加任何內容都可以產生合法的指針值; 此外,實現可以為此類操作定義任何有用行為的情況很少見,通常可以通過編譯器內在函數(*)更好地處理。 然而,在許多實現中,如果沒有捕獲空指針算術,則向空指針添加偏移量可以產生指針,該指針雖然無效,但不再可識別為空指針。 嘗試取消引用這樣的指針不會被捕獲,但可能會觸發任意效果。

捕獲表單(null + offset)和(null-offset)的指針計算將消除這種危險。 請注意,保護不一定需要捕獲(指針為空),(空指針)或(null-null),而前兩個表達式返回的值不太可能有任何用處[如果要實現指定null-null將產生零,針對該特定實現的代碼有時可能比必須特殊情況為null代碼更有效]它們不會生成無效指針。 此外,讓(null + 0)和(null-0)產生空指針而不是陷阱不會危及安全性並且可能避免需要使用用戶代碼特殊情況的空指針,但是由於編譯器的優點不那么引人注目將不得不添加額外的代碼來實現這一目標。

(*)例如,8086編譯器上的這種內在函數可能接受無符號的16位整數“seg”和“ofs”,並且在地址seg:ofs讀取單詞而沒有空陷阱,即使地址恰好為零。 8086上的地址(0x0000:0x0000)是某些程序可能需要訪問的中斷向量,而地址(0xFFFF:0x0010)在只有20個地址線的舊處理器上訪問與(0x0000:0x0000)相同的物理位置,在具有24個或更多地址線的處理器上訪問物理位置​​0x100000。 在某些情況下,另一種方法是對指針進行特殊指定,這些指針預期指向C標准未識別的內容(中斷向量將符合條件),並避免對這些內容進行空捕獲,或者指定volatile指針將以這種方式處理。 我已經在至少一個編譯器中看到了第一個行為,但是我不認為我已經看過第二個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM