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
参数相关的N1570部分:
§7.22.1.4/ 5如果主题序列具有预期的形式和 base的值为零,以#开头的字符序列 第一个数字被解释为一个整数常数 6.4.4.1的规则。 [...]指向最终字符串的指针存储在
endptr
指向的对象,前提是endptr
不为空 指针。§7.22.1.4/ 7如果主题序列为空或没有 预期形式,不进行转换;
nptr
的值是 存储在endptr
指向的对象中,前提是endptr
不是空指针。
这意味着endptr
需要指向一个对象,并且endptr
在某个时候被解除引用。例如,this implementation does so:
if (endptr != 0)
*endptr = (char *)(any ? s - 1 : nptr);
然而,this highly upvoted answer以及this man page都显示endptr
传递给strtol
未初始化。是否有一个异常会导致这种行为不明确?
答案 0 :(得分:9)
指针的值及其地址不相同。
void *foo;
该指针具有未定义的值,但foo
的地址,即&foo
的值必须是明确的(否则我们无法访问它)。
至少这是我的直觉理解,我现在没有挖掘标准,我只是认为你误读了它。
在谈论代码时,两者有时会混淆(“指针的地址是什么?”可以表示“指针的值是什么,它指向的是什么地址?”)但是他们真的很不同。
答案 1 :(得分:4)
在这个表达式中:
&ptr
&
操作数的地址,i。例如,ptr
对象的地址已生成,但永远不会评估ptr
对象。
(C11,6.3.2.1p2)“除非它是sizeof运算符,一元&amp;运算符,++运算符, - 运算符或。运算符的左操作数或赋值的操作数。运算符,没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值);这称为左值转换。“
答案 2 :(得分:2)
没有。它不是未定义的行为。只有ptr
未初始化且具有不确定的值,但&ptr
具有适当的值。
strtol
上的标准引言说:
...如果主题序列为空或者没有预期的形式,则不进行转换; nptr的值存储在endptr指向的对象中,前提是endptr不是空指针。
以上引用是关于这样一个电话的讨论:
strtol(str, 0, 10);
手册页中的调用和链接的答案完全没问题。
答案 3 :(得分:2)
参见此示例
char * ptr;
由于ptr
未指向任何对象,因此取消引用它会调用未定义的行为。但是当你将其地址传递给strtol
时,有语法
long int strtol(const char *nptr, char **endptr, int base);
在声明中
long parsed = strtol("11110111", &ptr, 2);
endptr
的{{1}}参数指向对象strtol
,并且取消它后将不会调用任何UB。
答案 4 :(得分:0)
我只能访问N1256,但如果有任何重大变化,我会感到惊讶。
最相关的部分是“6.5.3.2地址和间接操作符”
特别是第3段(我的重点):
语义
3 一元&amp;运算符产生其操作数的地址。如果 操作数具有类型''type'',结果具有类型''指向类型''的指针。如果 操作数是一元*运算符的结果,也不是该运算符的结果 也不是&amp;运算符被评估,结果就像两者都是 省略,除了对运算符的约束仍然适用和 结果不是左值。同样,如果操作数是结果 一个[]运算符,不是&amp;算子也不是一元的* 评估[]所暗示的结果,就好像&amp;操作者 被删除,[]运算符被更改为+运算符。 否则,结果是指向对象或函数的指针 由其操作数指定。
OP中引用的段落均未适用,因为许多人已指出。某事物的价值及其地址是非常不同的。
我认为对&amp; amp;没有任何限制关于未经初始化的价值,允许(通过没有禁令)。
注意:我们都知道这很好,但很难在这些标准中找到明确说“是的,你可以这样做等等。”