首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >指针删除后由指针保存的地址更改

指针删除后由指针保存的地址更改
EN

Stack Overflow用户
提问于 2013-12-22 00:40:38
回答 2查看 912关注 0票数 1

在下面的代码中,为什么指针x所持有的地址在delete之后会发生变化?据我所知,delete调用应该从堆中释放分配的内存,但不应该更改指针地址。

代码语言:javascript
运行
复制
using namespace std;
#include <iostream>
#include <cstdlib>

int main()
{
    int* x = new int;
    *x = 2;

    cout << x << endl << *x << endl ;

    delete x;

    cout << x << endl;

    system("Pause");
    return 0;
}

OUTPUT:
01103ED8
2
00008123

注意事项:我使用的是Visual 2013和Windows 8。据报道,这在其他编译器中是不一样的。另外,我知道这是个错误的做法,我应该在删除后重新分配指向NULL的指针,我只是想了解是什么导致了这种奇怪的行为。

EN

回答 2

Stack Overflow用户

发布于 2013-12-22 00:44:09

据我所知,删除应该将分配的内存从堆中释放出来,但不应该更改指针地址。

为什么不呢?这是完全合法的输出--删除后读取指针会导致未定义的行为。这包括指针的值变化。(实际上,这甚至不需要UB;deleted指针实际上可以指向任何地方。)

票数 5
EN

Stack Overflow用户

发布于 2013-12-22 15:26:34

阅读了C++98和C++11 N3485的相关部分,以及H2CO3指出的所有内容:

标准的两个版本都没有充分描述什么是“无效指针”,在什么情况下创建它们,或者它们的语义是什么。因此,我不清楚OP的代码是否意在引发未定义的行为,但事实上它确实是这样做的(因为标准没有明确定义的任何内容在同义上都是未定义的)。该文本在C++11中得到了改进,但仍然不够充分。

作为语言设计的一个问题,下面的程序确实表现出未指定的行为标记,这是很好的。它可能(但也不应该)显示标记的未定义行为;换句话说,如果该程序表现出未定义的行为,即标准中的一个缺陷,就不应该是UB。具体而言,复制“无效”指针的值并对这些指针执行相等比较的不应该是UB。我明确地拒绝了相反的论点,因为假设的硬件只会将指向未映射内存的指针加载到寄存器中。(注:我在C++11中找不到与C11 6.5.2.3脚注95相对应的文本,涉及编写一个工会成员和读取另一个工会成员的合法性;此程序假定此操作的结果未指定,但未定义(除非它可能涉及陷阱表示),如C中所示)。

代码语言:javascript
运行
复制
#include <string.h>
#include <stdio.h>

union ptr {
    int *val;
    unsigned char repr[sizeof(int *)];
};

int main(void)
{
    ptr a, b, c, d, e;

    a.val = new int(0);
    b.val = a.val;
    memcpy(c.repr, a.repr, sizeof(int *));

    delete a.val;
    d.val = a.val; // copy may, but should not, provoke UB
    memcpy(e.repr, a.repr, sizeof(int *));

    // accesses to b.val and d.val may, but should not, provoke UB
    // result of comparison is unspecified (may, but should not, be undefined)
    printf("b %c= d\n", b.val == d.val ? '=' : '!');

    // result of comparison is unspecified
    printf("c %c= e\n", memcmp(c.repr, e.repr, sizeof(int *)) ? '!' : '=');
 }

这是来自C++98的所有相关文本:

3.7.3.2p4如果标准库中给出的去分配函数的参数不是空指针值(4.10),那么解除分配函数将释放指针所引用的存储,从而使引用到已分配存储的任何部分的所有指针无效。未定义使用无效指针值(包括将其传递给解分配函数)的效果。脚注:在某些实现中,它会导致系统生成的运行时错误。

问题是,没有“使用无效指针值”的定义,因此我们需要讨论什么才是合格的。在讨论迭代器(定义为包含裸指针的类别)时,委员会的意图有一个线索:

24.1p5 ..。迭代器还可以具有与任何容器无关的奇异值。[示例:在未初始化指针x (与int* x; sic一样)声明后,必须始终假定x具有指针的单数值。]对于奇异值,大多数表达式的结果都是未定义的;唯一的例外是将非奇异值分配给具有奇异值的迭代器。在这种情况下,奇异值被覆盖的方式与任何其他值相同。可销毁的和过去的结束值总是非奇异的.

假设“无效指针”也意味着是“奇异迭代器”的例子,但没有文本支持这一点,这似乎至少是可信的;相反,没有文本证实(同样可信的)假设,即未初始化的指针值意味着“无效指针”以及“奇异迭代器”。因此,我们中间的分光器可能不会接受“大多数表达式的结果都是未定义的”来澄清什么是无效指针的使用。

C++11更改了相应于3.7.2.3p4的文本:

3.7.4.2p4 .通过无效指针值间接性和将无效指针值传递给去分配函数具有未定义的行为。无效指针值的任何其他使用都具有实现定义的行为。脚注:有些实现可能定义复制无效指针值会导致系统生成的运行时错误。

(省略号所省略的文本不变)我们现在对“使用无效指针值”的含义有了更明确的理解,我们现在可以说OP的代码的语义肯定是实现--定义了(但可能是实现)--定义为未定义)。在迭代器的讨论中还有一个新的段落:

24.2.1p10无效迭代器是可能是单数的迭代器。

这证实了“无效指针”和“奇异迭代器”实际上是相同的。C++11中剩下的困惑主要是关于产生无效/奇异指针/迭代器的确切情况;应该有一个指针/迭代器生命周期转换的详细图表(就像*值一样)。而且,与C++98一样,该标准存在缺陷,因为它不能保证对这些值的复制和相等比较是有效的(而不是未定义的)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20725030

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档