【问题标题】:Cannot load ntdll.dll [closed]无法加载 ntdll.dll [关闭]
【发布时间】:2016-12-12 08:24:52
【问题描述】:

我已经研究这个问题一段时间了,我想我已经缩小了我的问题范围。

这是错误输出

Critical error detected c0000374
Duke's Army.exe has triggered a breakpoint.

Exception thrown at 0x77E49841 (ntdll.dll) in Duke's Army.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77E7C8D0).
Unhandled exception at 0x77E49841 (ntdll.dll) in Duke's Army.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77E7C8D0).

The program '[14436] Duke's Army.exe' has exited with code 0 (0x0).

调用栈如下

ucrtbased.dll!0f8aa672()    Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for ucrtbased.dll] 
[External Code] 
>   Duke's Army.exe!Tile::Tile() Line 19    C++
[External Code] 
Duke's Army.exe!Map::Map(int w, int h) Line 70  C++
Duke's Army.exe!MapGenerator::init(int w, int h) Line 37    C++
Duke's Army.exe!MapGenerator::MapGenerator(int w, int h) Line 13    C++
Duke's Army.exe!PlayGameState::PlayGameState(Game * g) Line 13  C++
Duke's Army.exe!main() Line 11  C++
[External Code] 

其他答案建议删除未正确声明的静态成员或类似的东西。但是,在(假定的)受影响的类中,有一个静态向量,我找不到删除的方法。有什么建议吗?

[这是我认为发生错误的类] (调用栈第19行是默认构造函数定义的开始)

平铺.h

class Tile
{
public:
    static std::vector<Tile> tiles;

    // Constructors and methods...

    // Method used in constructors to add to static tiles       
    void Tile::init(const std::string& n, const sf::Color& c) {
        this->name = n;
        this->color = c;
        tiles.push_back(*this);
    }

    Tile(std::string n, sf::Color c) {
        init(n, c);
    };

    Tile() {
        init("bounds", sf::Color::Black);
    }

    const static Tile wall;
    const static Tile floor;
    const static Tile bounds;
    const static float TILE_SIZE;
};

静态成员在 Tile.cpp 中声明

std::vector<Tile> Tile::tiles = std::vector<Tile>(3);
const Tile Tile::wall("wall", sf::Color::White);
const Tile Tile::floor("floor", sf::Color::Green);
const Tile Tile::bounds;
const float Tile::TILE_SIZE = 16.f;

【问题讨论】:

  • 我认为问题不会出现在任何声明代码中。检查你的构造函数和方法。
  • const Tile Tile::xxxx 应该是const Tile::xxxx
  • @Jean-FrançoisFabre 嗯。我不这么认为。这些成员(其中三个)是Tile 对象,对于Tile 类是静态的(这是允许的)。
  • 调试器会出现这种情况吗?什么是调用栈?
  • 这发生在调试器中。我将发布带有答案的调用堆栈。

标签: c++ c++11 static global sfml


【解决方案1】:

您的代码默认初始化Tile::tiles,如下所示:

std::vector<Tiles> Tile::tiles = std::vector<Tile>(3);

vector 的构造不只是设置容量,它创建一个包含 3 个元素的向量,默认构造,这将导致对 initinit 的 3 次调用

tiles.push_back(*this);

push_back 尝试将向量增加一,然后复制构造新添加的元素。这里的关键部分是增加向量

再次提醒:请记住,这是在向量构建期间发生的。

您将创建一个超出矢量目标大小的新元素,或者覆盖当前正在填充的元素。

在构造函数完成之前,std::vector 的 GNU 实现不会设置向量大小。结果,您会被覆盖:

#include <iostream>
#include <string>
#include <vector>

struct S {
    std::string s_;
    static std::vector<S> tiles;

    S() { std::cout << "S()\n"; init("default"); }
    S(const std::string& s) {
        std::cout << "S(" << (void*) this << " with " << s << ")\n";
        init(s);
    }
    S(const S& rhs) {
        std::cout << (void*) this << " copying " << (void*)&rhs << " (" << rhs.s_ << ")\n";
        s_ = rhs.s_;
        s_ += " copy";
    }

    void init(const std::string& s) {
        s_ = s;
        std::cout << "init " << (void*)this << " " << s_ << "\n";
        tiles.push_back(*this);  // makes copy
    }
};


std::vector<S> S::tiles = std::vector<S>(3);

int main() {
    for (const auto& el : S::tiles) {
        std::cout << el.s_ << "\n";
    }
}

输出http://ideone.com/0dr7L2

S()
init 0x9e67a10 default
0x9e67a10 copying 0x9e67a10 ()
S()
init 0x9e67a14 default
0x9e67a14 copying 0x9e67a14 ()
S()
init 0x9e67a18 default
0x9e67a18 copying 0x9e67a18 ()
 copy
 copy
 copy

所以您在应用程序启动期间引入了 UB。

在上面的示例中,复制构造函数在执行复制之前默认初始化其目标,并且由于它正在复制自身,这导致 rhs.s_ 被清除。这就是我们得到“copy”而不是“default copy”的原因。

--- 编辑---

(无效,正如@underscore_d 指出的那样)

--- 编辑 2 ---

MSVC 向量实现是这样做的:

explicit vector(size_type _Count)
    : _Mybase()
    {   // construct from _Count * value_type()
    if (_Buy(_Count))
        {   // nonzero, fill it
        _TRY_BEGIN
        _Uninitialized_default_fill_n(this->_Myfirst(), _Count,
            this->_Getal());
        this->_Mylast() += _Count;
        _CATCH_ALL
        _Tidy();
        _RERAISE;
        _CATCH_END
        }
    }

关键部分是:

        _Uninitialized_default_fill_n(this->_Myfirst(), _Count,
            this->_Getal());
        this->_Mylast() += _Count;

在填充期间,您的 push_back 将增加 _MyLast 3 个位置,然后 ctor 的下一行将增加 3 个 _MyLast

下面是上面在 Visual Studio 下运行的相同代码:http://rextester.com/WNQ21225

【讨论】:

  • @user4581301 是的 - 另请参阅我对墙壁、地板和天花板的编辑。
  • “初始化顺序未定义”仅在不同翻译单元之间。这两个变量在单个 TU 中定义。在这种情况下,与任何其他static 对象一样,初始化顺序保证遵循定义顺序。您的最后一部分似乎无关紧要。
  • @user4581301 请参阅编辑 2,以及测试代码的在线 Visual Studio 版本的链接。
  • @underscore_d doh!及时纠正谢谢
猜你喜欢
  • 2018-03-15
  • 1970-01-01
  • 2013-02-08
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
  • 2019-05-12
  • 1970-01-01
  • 2014-09-20
相关资源
最近更新 更多