获取未初始化指针的地址是未定义的行为吗?

N1570声明这是未定义的行为:

§J.2/ 1具有自动存储持续时间的对象的值在不确定时使用(6.2.4,6.7.9,6.8)。

在这种情况下,我们的指针具有不确定的值:

§6.7.9/ 10如果没有显式初始化具有自动存储持续时间的对象,则其值是不确定的。 如果未显式初始化具有静态或线程存储持续时间的对象,则:

– 如果它有指针类型,则将其初始化为空指针;

然后,我假设以下测试程序显示未定义的行为:

#include  int main(void) { char * ptr; printf("%p", (void*)&ptr); } 

我的动机是strtolfunction。 首先,让我引用与endptr参数相关的endptr

§7.22.1.4/ 5如果主题序列具有预期forms且base的值为零,则根据6.4.4.1的规则将以第一个数字开头的字符序列解释为整数常量。 […]指向最终字符串的指针存储在endptr指向的对象中,前提是endptr不是空指针。

§7.22.1.4/ 7如果主题序列为空或没有预期的forms,则不进行转换; 如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。

这意味着endptr需要指向一个对象,并且该endptr在某个时候被解除引用。 例如, 此实现是这样做的 :

 if (endptr != 0) *endptr = (char *)(any ? s - 1 : nptr); 

然而, 这个高度赞成的答案以及这个手册页都显示endptr被传递给strtol未初始化。 是否有一个exception导致这种未定义的行为?

在这个表达式中:

&ptr

&操作数的地址,即ptr对象的地址被生成,但ptr对象永远不会被评估。

(C11,6.3.2.1p2)“除非它是sizeof运算符,一元&运算符,++运算符, – 运算符或。运算符或赋值运算符的左操作数的操作数,否则它是左值没有数组类型的数据被转换为存储在指定对象中的值(并且不再是左值);这称为左值转换。“

指针的值和它的地址不一样。

 void *foo; 

该指针有一个未定义的值,但foo的地址,即&foo的值,必须是明确的(因为否则我们无法访问它)。

至少这是我的直觉理解,我现在没有挖掘标准,我只是认为你误读了它。

在谈论代码时,两者有时会混淆(“指针的地址是什么?” 可能意味着“指针的值是什么,它指向的是什么地址?”)但它们确实是截然不同的。

看这个例子

 char * ptr; 

由于ptr没有指向任何对象,因此解除引用它会调用未定义的行为。 但是当你将其地址传递给strtol ,有语法

 long int strtol(const char *nptr, char **endptr, int base); 

在声明中

 long parsed = strtol("11110111", &ptr, 2); 

strtolendptr参数指向对象ptr并且derefeencing它不会调用任何UB。

不,这不是未定义的行为。 只有ptr是未初始化的并且具有不确定的值,但是&ptr具有适当的值。

strtol上的标准报价是什么:

…如果主题序列为空或者没有预期的forms,则不进行转换; 如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。

以上引用是关于这样的调用的讨论:

 strtol(str, 0, 10); 

手册页中的调用和链接的答案非常好。

我只能访问N1256,但如果有任何重大变化,我会感到惊讶。

最相关的部分是“6.5.3.2地址和间接操作符”

特别是第3段(我的重点):

语义

3 一元&运算符产生其操作数的地址。 如果操作数具有类型”type”,则结果具有类型”指向类型”的指针。 如果操作数是一元*运算符的结果,则不会对该运算符和&运算符进行求值,结果就好像两者都被省略,除了对运算符的约束仍然适用且结果不是左值。 类似地,如果操作数是[]运算符的结果,则[]运算符和[]隐含的一元*都不会被计算,结果就像删除了&运算符并且[]运算符被更改为a +运算符。 否则,结果是指向由其操作数指定的对象或函数的指针。

正如许多人所指出的那样,OP中引用的段落均未适用。 某物的价值及其地址是非常不同的。

我认为,允许(通过没有禁令)对未经初始化的值采取其地址没有任何限制。

注意:我们都知道这很好,但很难在这些标准中找到明确说“是的,你可以这样做”的陈述。