Cannot load ntdll.dll

-3

I have researched this question for a while now and I think I have narrowed my problem down.

This is the error output

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).

Call stack is as follows

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] 

Other answers suggest removing a static member that wasn't declared properly or something akin to that. However, in the (supposed) affected class, there is a static vector that I cannot find a way to remove. Any suggestions?

[This is the class I think the errors occurs from] (Line 19 in the call stack is the beginning of the definition of the default constructor)

Tile.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;
};

Static members are declared in 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;
c++
c++11
static
global
sfml
asked on Stack Overflow Aug 5, 2016 by user3131163 • edited Aug 6, 2016 by underscore_d

1 Answer

3

Your code default-initializes Tile::tiles like this:

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

That construction of vector doesn't just set capacity, it creates a vector containing 3 elements, default constructored, which will result in 3 calls to init, and in init you

tiles.push_back(*this);

push_back attempts to grow the vector by one and then copy-construct the newly added element. The key part here is grow the vector.

Again: remember, this is happening during the construction of the vector.

You will either create a new element beyond the target size of the vector or over-write the element currently being populated.

The GNU implementation of std::vector doesn't set the vector size until the constructor has been completed. As a result, you get overwrites:

#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";
    }
}

Outputs 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

So you are introducing UB during startup of your application.

In the above example, the copy constructor default-initializes its target before performing the copy, and since it's copying itself this results in rhs.s_ being cleared. This is why we get "copy" and not "default copy".

--- Edit ---

(invalid, as pointed out by @underscore_d)

--- Edit 2 ---

The MSVC vector implementation does this:

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
        }
    }

The key part being:

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

During the fill, your push_back's will increment _MyLast by 3 positions, and then the next line of the ctor will increment _MyLast by 3 more.

Here's the same code from above running under Visual Studio: http://rextester.com/WNQ21225

answered on Stack Overflow Aug 5, 2016 by kfsone • edited Aug 5, 2016 by kfsone

User contributions licensed under CC BY-SA 3.0