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