把未初始化的指针取地址是否属于未定义行为?

5

N1570规定这是未定义的行为:

§J.2/1 在使用具有自动存储期限的对象时,其值是不确定的(6.2.4、6.7.9、6.8)。

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

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

— 如果它具有指针类型,则将其初始化为null指针;

然后我假设以下测试程序会出现未定义的行为:

#include <stdio.h>

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

我的关注点是函数 strtol。首先,让我引用与参数 endptr 相关的 N1570 的部分:

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

§7.22.1.4/7 如果主体序列为空或不具有预期的形式,则不执行转换;如果 endptr 不是空指针,则将 nptr 的值存储在所指向的对象中。

这意味着必须将 endptr 指向一个对象,并且也需要在某个时刻对 endptr 进行解引用。例如,此实现 就这样做了。
if (endptr != 0)
    *endptr = (char *)(any ? s - 1 : nptr);

然而,这个高赞答案以及这个手册页面都显示未初始化的endptr被传递给了strtol。是否有例外情况使得这不是未定义行为?


2
你没有使用指针的值,而是仅使用存储指针值的变量的地址。这是明确定义的。 - Hans Passant
5个回答

9
指针的值和它的地址不是同一个东西。
void *foo;

那个指针具有未定义的值,但是foo的地址,即&foo的值必须被确定(否则我们无法访问它)。
至少这是我的直觉理解,我现在没有翻阅标准,我只是认为你误读了它。
在谈论代码时,有时会混淆两者(“那个指针的地址是什么?”可能意味着“那个指针的值是什么,它指向哪个地址?”),但它们确实是不同的。

4
在这个表达式中:
&ptr 返回的是&操作数即ptr对象的地址,但是ptr对象不会被求值。
(C11, 6.3.2.1p2) "除sizeof运算符、一元&运算符、++运算符、--运算符、.运算符的左操作数或赋值运算符之外,没有数组类型的lvalue在转换为指向其存储值的值后不再是lvalue;这称为lvalue转换。"

2
不,这不是未定义行为。只有“ptr”未初始化并具有不确定的值,但“&ptr”具有适当的值。
对于“strtol”的标准引用如下:
“……如果主题序列为空或没有预期的形式,则不执行转换; 如果endptr不是空指针,则nptr的值存储在指向的对象中。”
上述引用是针对以下调用的:
```c++ char *endptr; long int value = strtol(nptr, &endptr, base); ```
strtol(str, 0, 10);

这个man页面中的调用和链接的答案都非常好。


由于NULL通常被定义为0,并且标准明确允许使用**endptr作为NULL调用strtol,那么为什么不能执行strtol(str, 0, 10); - wolfPack88
我的回答有点误导人......它是对*...前提是endptr不是空指针*的回复。当然,它允许它为0。我会编辑那一部分。 - P.P

2
请看这个例子。
char * ptr; 

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

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

在语句中

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

strtol函数的endptr参数指向ptr对象,对其进行解引用不会引起任何未定义行为。


0

我只能访问N1256,但如果有任何实质性的变化,我会感到惊讶。

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

特别是第3段(我强调):

语义

3 一元&运算符返回其操作数的地址。 如果操作数的类型为“type”,则结果的类型为“指向type的指针”。如果操作数是一元*运算符的结果,则不评估该运算符或&运算符,并且结果就像两者都省略一样,除了运算符的约束仍然适用且结果不是lvalue。同样,如果操作数是[]运算符的结果,则不评估&运算符或[]隐含的一元*,并且结果就像删除&运算符并将[]运算符更改为+运算符一样。否则,结果是指定其操作数的对象或函数的指针。

因为正如许多人指出的那样,所引用的OP中的任何段落都不适用,因为某物的值和其地址非常不同。

我认为,对于未初始化值取地址没有任何限制是被允许的(通过没有禁止规定来证明)。

注意:我们都知道这是可以的,但在这些标准中很少能找到明确说明“可以这样做”的语句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接