[英]Is it undefined behavior to take the address of an uninitialized pointer?
N1570聲明這是未定義的行為:
§J.2/ 1具有自動存儲持續時間的對象的值在不確定時使用(6.2.4,6.7.9,6.8)。
在這種情況下,我們的指針具有不確定的值:
§6.7.9/ 10如果沒有明確初始化具有自動存儲持續時間的對象,則其值是不確定的。 如果未顯式初始化具有靜態或線程存儲持續時間的對象,則:
- 如果它有指針類型,則將其初始化為空指針;
然后,我假設以下測試程序顯示未定義的行為:
#include <stdio.h>
int main(void) {
char * ptr;
printf("%p", (void*)&ptr);
}
我的動機是strtol
功能。 首先,讓我引用與endptr
參數相關的endptr
:
§7.22.1.4/ 5如果主題序列具有預期形式且base的值為零,則根據6.4.4.1的規則將以第一個數字開頭的字符序列解釋為整數常量。 [...]指向最終字符串的指針存儲在
endptr
指向的對象中,前提是endptr
不是空指針。§7.22.1.4/ 7如果主題序列為空或者沒有預期的形式,則不進行轉換; 如果
endptr
不是空指針,則nptr
的值存儲在endptr
指向的對象中。
這意味着endptr
需要指向一個對象,並且該endptr
在某個時候被解除引用。 例如, 此實現是這樣做的 :
if (endptr != 0)
*endptr = (char *)(any ? s - 1 : nptr);
然而, 這個高度贊成的答案以及這個手冊頁都顯示endptr
被傳遞給strtol
未初始化。 是否有一個例外,這使得這不是未定義的行為?
指針的值和地址不一樣。
void *foo;
該指針有一個未定義的值,但foo
的地址,即&foo
的值,必須很好地確定(因為否則我們無法訪問它)。
至少這是我的直覺理解,我現在沒有挖掘標准,我只是認為你誤讀了它。
在談論代碼時,兩者有時會混淆(“指針的地址是什么?” 可能意味着“指針的值是什么,它指向的是什么地址?”)但它們確實是截然不同的。
在這個表達式中:
&ptr
&
操作數的地址,即ptr
對象的地址被生成,但ptr
對象永遠不會被計算。
(C11,6.3.2.1p2)“除非它是sizeof運算符,一元&運算符,++運算符, - 運算符或。運算符或賦值運算符的左操作數的操作數,否則它是左值沒有數組類型的數據被轉換為存儲在指定對象中的值(並且不再是左值);這稱為左值轉換。“
不,這不是未定義的行為。 只有ptr
是未初始化的並且具有不確定的值,但是&ptr
具有適當的值。
strtol
上的標准報價是什么:
...如果主題序列為空或沒有預期的形式,則不執行轉換; 如果endptr不是空指針,則nptr的值存儲在endptr指向的對象中。
以上引用是關於這樣的調用的討論:
strtol(str, 0, 10);
手冊頁中的調用和鏈接的答案完全沒問題。
看這個例子
char * ptr;
由於ptr
沒有指向任何對象,因此解除引用它會調用未定義的行為。 但是當你將其地址傳遞給strtol
,有了語法
long int strtol(const char *nptr, char **endptr, int base);
在聲明中
long parsed = strtol("11110111", &ptr, 2);
strtol
的endptr
參數指向對象ptr
並且derefeencing它不會調用任何UB。
我只能訪問N1256,但如果有任何重大變化,我會感到驚訝。
最相關的部分是“6.5.3.2地址和間接操作符”
特別是第3段(我的重點):
語義
3 一元&運算符產生其操作數的地址。 如果操作數具有類型''type'',則結果具有類型''指向類型''的指針。 如果操作數是一元*運算符的結果,則不會對該運算符和&運算符進行求值,結果就好像兩者都被省略,除了對運算符的約束仍然適用且結果不是左值。 類似地,如果操作數是[]運算符的結果,則[]運算符和[]所暗示的一元*都不會被計算,結果就像刪除了&運算符並且[]運算符被更改為a +運算符。 否則,結果是指向由其操作數指定的對象或函數的指針。
正如許多人所指出的那樣,OP中引用的段落均未適用。 某物的價值及其地址是非常不同的。
我認為,允許(通過沒有禁令)對未提出的未初始化值的限制沒有任何限制。
注意:我們都知道這很好,但很難在這些標准中找到明確說“是的,你可以這樣做”的陳述。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.