比较悬挂指针是否合法?
int *p, *q;
{
int a;
p = &a;
}
{
int b;
q = &b;
}
std::cout << (p == q) << '\n';
注意如何既p
和q
指向对象已经消失了。这合法吗?
比较悬挂指针是否合法?
int *p, *q;
{
int a;
p = &a;
}
{
int b;
q = &b;
}
std::cout << (p == q) << '\n';
注意如何既p
和q
指向对象已经消失了。这合法吗?
int*f(){int a;return &a;}
为return 0;
。
Answers:
简介:第一个问题是完全使用价值是否合法p
。
后a
已被破坏,p
获得所谓的无效指针值。N4430的报价(有关N4430的状态的讨论,请参见下面的“注释”):
当到达存储区域的持续时间的结尾时,表示已释放存储的任何部分地址的所有指针的值将变为无效指针值。
N4430的同一部分还介绍了使用无效的指针值时的行为(并且几乎相同的文本出现在C ++ 14 [basic.stc.dynamic.deallocation] / 4中):
通过无效指针值进行的间接传递以及将无效指针值传递给释放函数均具有未定义的行为。无效指针值的任何其他使用都具有实现定义的行为。
[脚注:某些实现可能定义复制无效的指针值会导致系统生成的运行时错误。—尾注]
因此,您将需要查阅实现的文档,以了解此处应该发生的情况(自C ++ 14起)。
上面引号中的术语“使用”表示必须从左值到右值转换,如C ++ 14 [conv.lval / 2]:
当将左值到右值转换应用于表达式e,并且glvalue所引用的对象包含无效的指针值时,该行为是实现定义的。
历史:在C ++ 11中,这是未定义而不是实现定义的;它由DR1438更改。有关完整的引号,请参见此帖子的编辑历史记录。
适用于p == q
:假设我们在C ++ 14 + N4430中接受了评估的结果,p
并且q
是实现定义的,并且实现未定义发生硬件陷阱;[expr.eq] / 2说:
如果两个指针都为null,都指向同一函数或都表示相同的地址(3.9.2),则两个指针比较相等,否则,它们比较不相等。
由于它的实现定义什么值时获得p
和q
评估,我们不能肯定地说这里会发生什么。但是它必须是实现定义的或未指定的。
在这种情况下,g ++似乎表现出未指定的行为;取决于-O
开关,我可以说是1
还是0
,对应于销毁b
后a
是否重复使用相同的内存地址。
关于N4430的注意事项:这是针对C ++ 14的建议的缺陷解决方案,尚未得到接受。它清除了有关对象生存期,无效指针,子对象,联合和数组边界访问的许多措辞。
在C ++ 14文本中,它在[basic.stc.dynamic.deallocation] / 4和后续段落中定义,使用该指针时会出现无效的指针值delete
。但是,没有明确说明相同的原理是否适用于静态或自动存储。
[basic.compound] / 3中有一个“有效指针”的定义,但是它太模糊了,无法明智地使用。[basic.life] / 5(脚注)指的是同一文本,它定义了指向对象对象的指针的行为。静态存储持续时间,这表明它适用于所有类型的存储。
在N4430中,文本从该部分上移了一层,因此它确实适用于所有存储期限。附有注释:
注释:这应适用于所有可能结束的存储期限,而不仅仅是动态存储期限。在支持线程或分段堆栈的实现中,线程和自动存储的行为可能与动态存储的行为相同。
我的意见:除了说p
获取了无效的指针值外,我没有其他一致的解释标准的方法(N4430之前的版本)。除了我们已经看过的内容外,其他任何部分似乎都没有涵盖该行为。因此,在这种情况下,我很高兴将N4430措辞视为代表该标准的意图。
从历史上看,有些系统使用指针作为右值可能导致系统获取该指针中某些位标识的某些信息。例如,如果一个指针可能包含对象标头的地址以及该对象的偏移量,则获取指针可能会导致系统也从该标头中获取一些信息。如果该对象已不存在,则尝试从其标头中获取信息的尝试可能会失败,并产生任意后果。
话虽这么说,在绝大多数的C实现中,在某个特定时间有效的所有指针将永远保持与该特定时间相同的关系和减法运算符关系。确实,在大多数实现中,如果有的话char *p
,可以char *base; size_t size;
通过检查是否(size_t)(p-base) < size
;确定是否标识了对象的一部分。如果对象的寿命存在任何重叠,则这种比较甚至可以追溯地进行。
不幸的是,该标准没有定义代码可以表明它需要后一种保证的任何方式,也没有一种标准的方法可以使代码询问特定的实现是否可以保证后者的任何行为并拒绝编译(如果它不这样做)。 。此外,某些超现代的实现将把对两个指针的关系运算符或减法运算符的任何使用都视为程序员的承诺,即所讨论的指针将始终标识相同的活动对象,并省略仅在该假设下才有意义的任何代码。没有举行。因此,即使许多硬件平台都能够提供对许多算法都有用的保证,
指针包含它们引用的变量的地址。即使释放/销毁/不可用曾经存储在其中的变量,地址也有效。只要您不尝试使用这些地址上的值,就可以安全使用,这意味着* p和* q将是未定义的。
显然,结果是实现定义的,因此如果不想深入研究汇编代码,则可以使用此代码示例来研究编译器的功能。
这是否有意义,完全是不同的讨论。