将指针转换为 std::uint64_t 并再次转换回指针时出现内存问题

Memory issues when converting a pointer to std::uint64_t and back to a pointer again

提问人:tongstar 提问时间:5/2/2023 更新时间:5/2/2023 访问量:133

问:

代码如下所示。

struct tNode
{
    int data;
    tNode* next;
    tNode(const int& data, tNode* next = nullptr)
    {
        this->data = data;
        this->next = next;
    }
};

int main()
{
    tNode* ptr = new tNode(1);
    std::uint64_t num_ptr = reinterpret_cast<std::uint64_t>(ptr);
    delete ptr;
    auto back_to_ptr = reinterpret_cast<tNode*>(num_ptr);
    tNode* ret = back_to_ptr->next;
    std::cout << ret;
}

当我尝试使用 MSVC 编译器在 Visual Studio 2022 中进行调试时,我无法检测到任何错误。 我相信应该输出有关内存访问冲突的错误信息,那么为什么上面的代码工作正常?tNode* ret = back_to_ptr->next;

我一直在寻找将指针转换为 int 类型的问题。 使用 Visual Studio 2022,我通过获取断点来调试上述示例。

C++ 指针内存 转换 访问冲突

评论

1赞 Etienne de Martel 5/2/2023
好吧,这是未定义的行为,所以谁知道会发生什么。
1赞 user4581301 5/2/2023
delete ptr;告诉系统,“我已经完成了这个,并保证我再也不会看它了。在方便的时候,可以随意发布或重复使用它。A) 你违背了诺言!B) 系统尚未发现释放或重用内存不方便,因此不会引发错误。
1赞 François Andrieux 5/2/2023
这与指针转换无关。你可以直接尝试。无论哪种情况,您都违反了规则,并且可能导致任何行为,包括不崩溃。但这种行为基本上是一种随机的可能结果,它可能随时无缘无故地改变。错误在于期望 C++ 在违反规则时崩溃。有时它工作不正确,有时似乎工作,以及其他阴险的结果。ptr->next;
1赞 Peter 5/2/2023
这是未定义的行为。C++的初学者(甚至太多的老师)通常认为“未定义的行为”意味着“某种形式的崩溃或记忆违规”。实际上,未定义的行为意味着“C++标准不对发生的事情施加任何约束”。因此,未定义的行为可以以多种方式表现出来。一种方法是“看似有效并通过所有测试”。另一种方式是某种形式的内存相关错误。一个更阴险的表现是“似乎通过开发计算机上的所有测试来工作,但破坏了客户计算机上的关键数据”。
2赞 user4581301 5/2/2023
旁注:目前,将指针转换为指针是相当安全的。但是过去将指针转换为 是相当安全的,所以如果明天在你的脸上塞进指针,不要感到惊讶。更喜欢使用 ,一个保证能够保存指针的整数。uint64_tintuint64_tuintptr_t

答:

2赞 templatetypedef 5/2/2023 #1

幸运的是,这里的问题与从整数到指针的转换无关,反之亦然。相反,问题在于线不会对自己做任何事情,而是破坏它所指向的对象。对该对象的任何进一步使用,无论如何获取指向该对象的指针,都归类为未定义的行为。这意味着您可能会收到内存错误,也可能不会。在你的例子中,碰巧你不会遇到崩溃或访问冲突,即使你正在做的事情清楚地访问了一个对象的生命周期结束后。delete ptrptr

评论

1赞 user4581301 5/2/2023
所有这些都是以效率的名义完成的(或者更确切地说是没有完成的)。这种情况是微不足道的,Mark I 眼球很快就把它挑出来了。在一般情况下,检查以确保指向的对象没有被释放,这是报告错误所必需的,必须到处都吓坏了,这将严重影响性能。C++ 有一个策略,即不让你为没有明确要求的东西付费,所以在大多数情况下,不会进行任何检查,编译器在假设你知道自己在做什么的情况下编写代码。