【问题标题】:Unique_ptr: pointer being freed was not allocated when emplaced in listUnique_ptr:被释放的指针在放入列表时未分配
【发布时间】:2018-08-01 15:34:01
【问题描述】:

我有一个像这样的简单 C++ 程序:

#include <iostream>
#include <list>
#include <memory>

int main(void) {
  std::list<std::unique_ptr<int>> l;
  int x = 5;
  // throws runtime error: pointer being freed was not allocated
  l.emplace_back(&x);
}

当程序运行时,我得到以下输出:

a.out(59842,0x7fffb13d1380) malloc: *** error for object 0x7ffee81489ac: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug [1] 59842 abort ./a.out

这告诉我,在销毁列表期间,已创建的 unique_ptr 对象被销毁,而在销毁期间,指向 x 的指针被释放,而它从未被 malloced 释放。

但是我希望在unique_ptr 超出范围并且是destroyed 的情况下也会发生同样的情况:

#include <iostream>
#include <list>
#include <memory>

int main(void) {
  int x = 5;

  // does not throw runtime error when falling out of scope and being destructed
  std::unique_ptr<int> p(&x);
}

但是,当上面的unique_ptr p 超出范围并被销毁时,程序运行良好。为什么第一个程序在销毁unique_ptr 时给我一个错误,而第二个程序却没有?为什么在列表中放置 unique_ptr 会导致其破坏失败?

【问题讨论】:

  • 两者都是 UB,因此行为可能不同。

标签: c++ c++11 unique-ptr


【解决方案1】:

未定义的行为可能导致任何事情,从崩溃到明显成功的运行,再到英国投票退出欧盟(哎呀,对不起,伙计们……空指针取消引用可以做的事情真是太神奇了)。

编译器是极其复杂的东西。您的程序不是将由目标计算机按顺序执行的指令的一对一映射;它大致描述了您希望计算机如何运行。两者之间的转换非常深奥和复杂。当你走下坡路时,你会以这样一种方式破坏语言:编译器的内部工作——不断做出假设并采取它知道可以做的捷径,尤其是在应用所谓的“优化”时——被打破了不可预知的方式。

这是其中之一。

简而言之,如果您期望 UB 做任何特别的事情,那么您已经失败了。

通过分析编译器的源代码和生成的程序集,可以准确确定您的 UB 在这种情况下的行为方式。但这将是一个漫长、艰巨且最终完全没有意义的过程。

只是不要从你没有动态分配的东西中创建智能指针(或者,如果你出于某种原因真的想要,提供一个自定义删除器,它不会尝试 delete 不是 @ 的东西987654322@d)。简单!

【讨论】:

    【解决方案2】:

    我认为发生的事情是第二个示例中的变量 p 刚刚被优化掉,因此不会导致运行时错误。

    但是发生的错误很明显,它正在尝试释放在堆栈上分配的内存。请注意,智能指针通常与堆分配的内存一起使用,而不是在堆栈上分配的内存。

    【讨论】:

    • 您可能可以使用无操作删除器使其与堆栈分配的对象一起正常工作......只是出于好奇。
    • 你是对的。使用 -O0 运行会产生错误。然而,尽管我认为 volatile 限定符会阻止优化,但使用带有 -O3 的 volatile std::unique_ptr&lt;int&gt; p(&amp;x); 运行仍然不会产生错误。
    • @RaeesRajwani Making p volatile 在这里没有什么有趣的事情。
    • @RaeesRajwani 这是对volatile 所做工作的过度简化,并且可能是您的整个程序被优化了,因为它什么都不做。 (在列表的情况下,这对您的编译器来说更难证明,所以也许它不会打扰。)
    • @RaeesRajwani 确实如此,但它仍然没有改变他的代码不必崩溃的事实。未定义的行为意味着不需要发生错误。
    【解决方案3】:

    我已经复制并编译了你的代码:

    $ g++ omg.cpp && ./a.exe
    Aborted (core dumped)
    

    您可以尝试添加另一个级别的间接:

    int main(void) {
        int x = 5;
        {
            std::unique_ptr<int> p(&x);
        }
    }
    

    看看有没有帮助。

    【讨论】:

    • 当然它和我的 5.3 一样...当你运行二进制文件时会发生什么?
    • 我没有运行,因为godbolt 只允许编译代码但不允许运行它。但即使我运行它,它也会抛出同样的错误,因为释放分配在堆栈上的内存只是一个禁忌。
    • 这不是间接级别。
    • @ProXicT 不,您不能在这里依赖错误/诊断(如问题所示)。
    • @LightnessRacesinOrbit 是的,你是对的。我的意思是释放堆栈内存可能会导致任何结果。
    猜你喜欢
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 2020-09-25
    • 2018-09-30
    相关资源
    最近更新 更多