比较悬挂指针是否合法?


70

比较悬挂指针是否合法?

int *p, *q;
{
    int a;
    p = &a;
}
{
    int b;
    q = &b;
}
std::cout << (p == q) << '\n';

注意如何既pq指向对象已经消失了。这合法吗?


11
定义“合法”。
2015年

5
至少不是未定义的行为。
小狗

43
@rightfold我冒着被语言律师停下来的风险吗?
fredoverflow

5
作为数据点,gcc优化int*f(){int a;return &a;}return 0;
Marc Glisse 2015年

5
我想知道这样做的用处
Ed Heal 2015年

Answers:


57

简介:第一个问题是完全使用价值是否合法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),则两个指针比较相等,否则,它们比较不相等。

由于它的实现定义什么值时获得pq评估,我们不能肯定地说这里会发生什么。但是它必须是实现定义的或未指定的。

在这种情况下,g ++似乎表现出未指定的行为;取决于-O开关,我可以说是1还是0,对应于销毁ba是否重复使用相同的内存地址。


关于N4430的注意事项:这是针对C ++ 14的建议的缺陷解决方案,尚未得到接受。它清除了有关对象生存期,无效指针,子对象,联合和数组边界访问的许多措辞。

在C ++ 14文本中,它在[basic.stc.dynamic.deallocation] / 4和后续段落中定义,使用该指针时会出现无效的指针值delete。但是,没有明确说明相同的原理是否适用于静态或自动存储。

[basic.compound] / 3中有一个“有效指针”的定义,但是它太模糊了,无法明智地使用。[basic.life] / 5(脚注)指的是同一文本,它定义了指向对象对象的指针的行为。静态存储持续时间,这表明它适用于所有类型的存储。

在N4430中,文本从该部分上移了一层,因此它确实适用于所有存储期限。附有注释:

注释:这应适用于所有可能结束的存储期限,而不仅仅是动态存储期限。在支持线程或分段堆栈的实现中,线程和自动存储的行为可能与动态存储的行为相同。


我的意见:除了说p获取了无效的指针值外,我没有其他一致的解释标准的方法(N4430之前的版本)。除了我们已经看过的内容外,其他任何部分似乎都没有涵盖该行为。因此,在这种情况下,我很高兴将N4430措辞视为代表该标准的意图。



24
@LightnessRacesinOrbit请给我买标准的副本,以便我能做到(如果您可以邮寄给我印刷版本,那将是很好的选择,因此我可以在答案中显示实际的标准,而不仅仅是其内容,这似乎是与您无关(内容是我的意思))。顺便说一句,菲利普说,他也将对印刷版感兴趣。
Griwes 2015年

19
我们其余的人不购买标准。我们引用了最新的免费可用的草稿,通常是FDIS左右,但是此类事项的措词并不会发生太大变化。
小狗

19
@LightnessRacesinOrbit如果您知道Nxxxx文档,FDIS和正式标准之间的区别,那么您应该识别与最接近官方标准的近似值相对应的N编号,该数字可以免费在线免费获得。期望人们花费几百美元只是为了具有更多的说服力,这简直太荒谬了。
zwol 2015年

13
@zwol:实际上,规定任何进入壁垒是很合理的,以便击倒某人,这相当于一个小额赌注。关键是要赢,而不是正确;-)如果得到正确答案是关键,那么Lightness当然可以说“ ...公开的标准是相同/不同的”,而不是试图抹黑报价而不替换它。我的意思是,我认为Lightness是正确的,但是Filip引用的问题在于它们不支持他的主张,而不是不准确。
史蒂夫·杰索普

9
@LightnessRacesinOrbit就我个人而言,我对N3936的引用非常满意,除非有人专门复制C ++ 14并指出其中的区别(其中AFAIK没有)。C ++ 11和N3337也是如此。
MM 2015年

4

从历史上看,有些系统使用指针作为右值可能导致系统获取该指针中某些位标识的某些信息。例如,如果一个指针可能包含对象标头的地址以及该对象的偏移量,则获取指针可能会导致系统也从该标头中获取一些信息。如果该对象已不存在,则尝试从其标头中获取信息的尝试可能会失败,并产生任意后果。

话虽这么说,在绝大多数的C实现中,在某个特定时间有效的所有指针将永远保持与该特定时间相同的关系和减法运算符关系。确实,在大多数实现中,如果有的话char *p,可以char *base; size_t size;通过检查是否(size_t)(p-base) < size;确定是否标识了对象的一部分。如果对象的寿命存在任何重叠,则这种比较甚至可以追溯地进行。

不幸的是,该标准没有定义代码可以表明它需要后一种保证的任何方式,也没有一种标准的方法可以使代码询问特定的实现是否可以保证后者的任何行为并拒绝编译(如果它不这样做)。 。此外,某些超现代的实现将把对两个指针的关系运算符或减法运算符的任何使用都视为程序员的承诺,即所讨论的指针将始终标识相同的活动对象,并省略仅在该假设下才有意义的任何代码。没有举行。因此,即使许多硬件平台都能够提供对许多算法都有用的保证,


-3

指针包含它们引用的变量的地址。即使释放/销毁/不可用曾经存储在其中的变量,地址也有效。只要您不尝试使用这些地址上的值,就可以安全使用,这意味着* p和* q将是未定义的。

显然,结果是实现定义的,因此如果不想深入研究汇编代码,则可以使用此代码示例来研究编译器的功能。

这是否有意义,完全是不同的讨论。


这不仅仅是简单的“合法”,而是“实现定义”。
Mark Hurd

1
我同意(p == q)的结果是“实现定义的”。
user2038893 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.