-2

I'm working on creating a text based Windows game, and I am having a problem (I guess) with arrays not working the same within a class scope as within the main function. As far as I can tell it is some kind of interaction between a larger array class member (or large total amount of variables) and Windows creating a DC or other Windows API calls and/or variables.

What I want to do is a have a class called Map that contains a two dimensional array of Tiles. Tile is just a simple struct with basic tile information. I would like to make the array 256 x 256. This shouldn't be a problem as far as I can figure. Each Tile should be 32 bytes. That's 2 MB total for the array.

However, the game crashes when I declare a variable of the Map class in the main function, and then do things with Windows DCs. The return value seems to vary, In the current form, it usually returns 255, but I have also gotten "process terminated with status -1073741571". A 128 x 128 array does work in the class though. It also works fine if I remove either the array or the code in DisplayScreen. And as I implied, it also works if I just move the array of Tiles to the main function.

I'm honestly baffled. I have no idea what the difference would be. Nothing is going out of scope. Doesn't matter if it is a public or private member. Non dynamic class members should all get declared on the stack and it shouldn't work any differently in a class versus otherwise, right?

For other information, I am using Code::Blocks with the Min GW compiler. Everything is up to date. I am running Windows 10. My computer specs shouldn't be an issue either, but if it matters, I have 16 GB memory and a 4Ghz Athlon FX 8 core processor.

Edit: Here is the full code, so nothing is left out

Game.h:

#ifndef GAME_H_INCLUDED
#define GAME_H_INCLUDED

struct Tile
{
    char chr[2];
    int  r[2], b[2], g[2];

    bool solid;
    bool translucent;
    int  opacity;
};

class Map
{
    Tile tileMap[256][256];

public:
    Map();

};

Map::Map()
{
    int i, j;

    for(i=0;i<256;i++)
    {
        for(j=0;j<256;j++)
        {
            tileMap[i][j].chr[0] = 'X';
            tileMap[i][j].b[0] = 255;
            tileMap[i][j].r[0] = 255;
            tileMap[i][j].g[0] = 255;

            tileMap[i][j].chr[1] = ' ';
            tileMap[i][j].b[1] = 0;
            tileMap[i][j].r[1] = 0;
            tileMap[i][j].g[1] = 0;

            tileMap[i][j].solid = false;
            tileMap[i][j].translucent = false;
            tileMap[i][j].opacity = 255;
        }
    }
}

main.cpp:

#include <windows.h>
#include "Game.h"

#define FRAMERATE 60

//Function declarations
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
void DisplayScreen(HWND pWnd, Map &pMap);

//Make the class name into a global variable
char strClassName[ ] = "GameApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpstrArgument,
                    int nCmdShow)
{
    HWND hWnd;               //This is the handle for our window
    MSG messages;            //Here messages to the application are saved
    WNDCLASSEX wndClassEx;   //Data structure for the windowclass

    Map test;

    DWORD  sysTimer;
    DWORD  sysPrevTime = 0;
    DWORD  timerDelta = 1000 / FRAMERATE;

    //Get a handle for the whole screen
    HDC hDC = GetDC(NULL);

    //Initalize the Window structure
    wndClassEx.hInstance = hThisInstance;
    wndClassEx.lpszClassName = strClassName;
    wndClassEx.lpfnWndProc = WindowProcedure;
    wndClassEx.style = CS_DBLCLKS;
    wndClassEx.cbSize = sizeof (WNDCLASSEX);
    wndClassEx.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wndClassEx.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wndClassEx.hCursor = LoadCursor (NULL, IDC_ARROW);
    wndClassEx.lpszMenuName = NULL;                 //No menu
    wndClassEx.cbClsExtra = 0;
    wndClassEx.cbWndExtra = 0;
    wndClassEx.hbrBackground = CreateSolidBrush(RGB(0,0,0));

    //Register the window class, and if it fails quit the program
    if (!RegisterClassEx (&wndClassEx))
        return 0;

    //Create Window with registered window class
    hWnd = CreateWindowEx (
        0,                           
        strClassName,                //Class name
        "Game Test",                 //Title Text
        WS_OVERLAPPEDWINDOW,         //default window type
        0,                           //X pos of window at top left
        0,                           //Y pos of window at top left
        GetDeviceCaps(hDC, HORZRES), //Set window width to screen width
        GetDeviceCaps(hDC, VERTRES), //Set window height to screen height
        HWND_DESKTOP,                //Child-window to desktop
        NULL,                        //No menu
        hThisInstance,               //Program Instance handler
        NULL);                       //No Window Creation data


    //Removes borders from the window
    SetWindowLong(hWnd, GWL_STYLE, WS_POPUP);
    //Make the window visible on the screen
    ShowWindow (hWnd, nCmdShow);

    //Run the message and game loop
    while (true)
    {
        while(PeekMessage(&messages,NULL,0,0, PM_REMOVE))
        {
            if (messages.message == WM_QUIT)
            {
                ReleaseDC(NULL, hDC);
                DestroyWindow(hWnd);
                return 0;
            }

            TranslateMessage(&messages);
            DispatchMessage(&messages);
        }

        sysTimer = timeGetTime();

        if (sysTimer >= (sysPrevTime + timerDelta) )
        {
            sysPrevTime = sysTimer;
            DisplayScreen(hWnd, test);
        }

    }
}

//This function is called by the Windows function DispatchMessage()
LRESULT CALLBACK WindowProcedure (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_DESTROY:
            PostQuitMessage (0); //Send WM_QUIT to the message queue
            break;

        default:
            return DefWindowProc (hWnd, message, wParam, lParam);
    }

    return 0;
}

void DisplayScreen(HWND pWnd, Map &pMap)
{
    HDC hDC = GetWindowDC(pWnd);
    HDC hdcBuf = CreateCompatibleDC(hDC);

    HBITMAP hbmBuf = CreateCompatibleBitmap(hDC, 800, 600);
    HFONT hMapFont = CreateFont(17,11,0,0,400,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH | FF_MODERN,"Lucida Console");
    HFONT hTxtFont = CreateFont(17,11,0,0,400,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH | FF_MODERN,"Lucida Console");

    SelectObject(hdcBuf, hbmBuf);
    SelectObject(hdcBuf, hMapFont);

    SetBkColor(hdcBuf, RGB(0,0,0));
    SetTextColor(hdcBuf, RGB(255,255,255));

    //Draw to the buffer
    TextOut(hdcBuf, 10, 10, "Hello World   @", 15);

    //Tranfers the buffer to the Screen
    BitBlt(hDC, 100, 100, 800, 600, hdcBuf, 0, 0, SRCCOPY);

    //Release all object handles
    DeleteObject(hTxtFont);
    DeleteObject(hMapFont);
    DeleteObject(hbmBuf);

    DeleteDC(hdcBuf);
    ReleaseDC(pWnd, hDC);
}

It crashes with even one instance of something creating a DC. It works fine otherwise creating and destroying the DCs and displaying the bitmap over and over again even if I leave it for an hour. Once I create that class with the large array in it though, it just dies.

I actually used to have the Display function as a class function and I moved it out because I thought that was the problem, but it wasn't.

Interestingly, if I change the declaration from 'Map test;' to 'Map* test = new Map;' and change the rest of the program appropriately, it works. Honestly though, doing that just seems kind of dumb, and I think that would slow everything down if I don't have a good reason to put everything on the heap. Plus, I don't like bandages. If there is a problem I'd rather fix it.

Any ideas?

  • 1
    Post a [MCVE] please. – πάντα ῥεῖ Oct 07 '16 at 19:05
  • you could have written down sample of code not this such bla bla! – Raindrop7 Oct 07 '16 at 20:30
  • This is my first post ever on this website. Please be patient. I tried be fairly specific, and I didn't want it to be too much to read. I have a tendency to be verbose. I will edit my original post to include more code here soon. The main thing is though, it seems to work one way and not the other. Everything should be initialized correctly as well. – Chris Wood Oct 07 '16 at 20:43
  • timeGetTime() just returns the number of milliseconds the system has been running. It is in in Winmm.lib. GetTickCount() does much the same thing. What compiler were you using? I was formerly used to both Borland and Visual Studio. I am much less familiar with Min GW and if there are weird quirks responsible for this, I would have no idea. – Chris Wood Oct 07 '16 at 21:50
  • Never mind. I made a mistake earlier, I removed `map` because it wasn't being used, but that's the problem itself. – Barmak Shemirani Oct 07 '16 at 21:59
  • What the heck? Why are people down voting my question? "No research or effort"? I've done plenty of testing and trying multiple different things and I put a whole bunch of effort into the post. I've seen plenty of other posts that have a lot less detail. This is the first freaking post I've made on this website. I've even edited it. – Chris Wood Oct 08 '16 at 01:25

2 Answers2

0

You have a stack overflow (the condition, not the website).

  • If that is the case, I am not understanding why I get stack overflow with the array in the class, but not in WinMain(). I can even make a Tile array in WinMain that is 100 times the size. Aren't they both on the stack? I tried the same thing with a simple int static array. It let me make an int array in WinMain that was 256 x 256 x 256 x 16. The compiler actually complained when I tried to make it bigger. When I made a 256 x 256 x 16 int array in the class it compiled fine, it just crashed. – Chris Wood Oct 07 '16 at 20:41
  • @ChrisWood add code then if the matter is the size of array on the stack you can use heap (dynamic memory) which is big enough to do your purpose. – Raindrop7 Oct 07 '16 at 21:08
  • Unless I can figure out how to fix this, I will have to do that. I'm not 100% sure there isn't some other problem though, as I don't understand why it works in WinMain, but not in the Map class. I posted code btw. The thing that frustrates me is that heap memory is slower. Even though this is a text based game, I am wanting to do some fancy things with it. I mean, yeah, on modern computers it probably won't impact it. But if I do something later on down the line with actual graphics, especially if it is something 3D, I'd like to be sure I can make it run faster if possible. – Chris Wood Oct 07 '16 at 21:29
0

The problem can be reproduced in this program:

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    Map test;
    return 0;
}

It fails because it reaches stack limit.

Also tileMap[i][j].chr[2] is out of bound. It is declared as char chr[2]; valid index is 0 and 1. It can only go up to tileMap[i][j].chr[1]

Ditto b[], r[], and g[]

Change the Map class so that it allocates memory on heap and fix chr:

class Map
{
    //Tile tileMap[256][256];
    Tile **tileMap;
public:
    Map();
    ~Map();
};

Map::Map()
{
    int i, j;

    tileMap = new Tile*[256];
    for (i = 0; i < 256; i++)
        tileMap[i] = new Tile[256];

    for (i = 0; i<256; i++)
    {
        for (j = 0; j<256; j++)
        {
            //tileMap[i][j].chr[1] = 'X';
            tileMap[i][j].chr[0] = 'X';  //<== meant to be 0?
            tileMap[i][j].b[0] = 255;
            tileMap[i][j].r[0] = 255;
            tileMap[i][j].g[0] = 255;

            //tileMap[i][j].chr[2] = ' ';
            tileMap[i][j].chr[1] = ' '; //<== meant to be 1?
            tileMap[i][j].b[1] = 0;
            tileMap[i][j].r[1] = 0;
            tileMap[i][j].g[1] = 0;

            tileMap[i][j].solid = false;
            tileMap[i][j].translucent = false;
            tileMap[i][j].opacity = 255;
        }
    }
}

Map::~Map()
{
    int i = 0;
    for (i = 0; i < 256; i++)
        delete[]tileMap[i];
    delete[]tileMap;
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • I had tried this at one point, but switched it because then my program just hung up (no crash). Which is weird because it will let me allocate the whole Map on the heap. Let me try it again and make sure I am doing everything correctly. Still though, why would it reach stack limit in the class but not directly in WinMain? – Chris Wood Oct 07 '16 at 22:10
  • Yeah, I literally just copied this and it froze. I'm going to have to try and step through it later to see if I can see anything, but I don't understand it. Maybe some other program that is running has an issue. Or maybe I just have a bad sector of my ram, which would make me sad... – Chris Wood Oct 07 '16 at 22:23
  • You also out of bound variables. I ran the corrected code on both Code::Block and Visual Studio and it was fine. "*Maybe some other program that is running has an issue*" That's very unlikely unless you are running the program on Windows 95,or if your system has serious issues. The problem is more likely within your code. – Barmak Shemirani Oct 07 '16 at 22:53
  • I know I accidentally used 1 and 2 instead of 0 and 1, an unfortunate carry over from my VB days, but I have since changed that. It didn't fix the issue. If we are running the exact same code, I don't know why else this would be a problem. Like I said, I have the problem when part of the code is creating a DC, and when I remove either the DC or the array, it works. It also works for me with the array (even if it is larger) in WinMain. Dynamically allocating the array in the class doesn't work for me for some reason, but dynamically allocating the class itself does... – Chris Wood Oct 08 '16 at 01:16
  • If you dynamically allocate `Map`, then data is created on heap, not stack. So it will work because heap has a larger limit. The way I did it is basically the same thing. It's not going to crash for "some reason" or for no reason. It's not based on superstition and magic. There are other issues with your message loop and the rest of it, but it should not cause any crash. Maybe somebody else will help you, but try not to annoy them by posting buggy code and then telling that that your own private version of the code didn't have those bugs. – Barmak Shemirani Oct 08 '16 at 01:45
  • I know that. I'm not trying to annoy anyone. But the whole point is that I am trying to figure out what the bug is. And I never implied anything about magic. I implied I don't know what the problem is. I said I copied your code exactly, and it didn't work for me. I don't know why. – Chris Wood Oct 08 '16 at 01:50
  • Not sure why I would post a question here if everything worked fine. – Chris Wood Oct 08 '16 at 01:51
  • And again, I can create a very large non dynamically allocated array within WinMain. I just can't within the class. Both should be on the stack from what I understand. Maybe I'm wrong about that. If that is the case, then if someone can just not be confrontational and let me know, great. If they are both on the stack and one overflows and another doesn't, I'm just trying to figure out why. – Chris Wood Oct 08 '16 at 01:57
  • If it's dynamically allocated then it's always on heap, not on stack. Perhaps you think because local variables are on stack, then everything else within the same scope is also on stack, but that's not the case. Read [here](http://stackoverflow.com/questions/8468210/stack-vs-heap-c) That's a general c++ question, it's not exclusive to Windows. – Barmak Shemirani Oct 08 '16 at 02:36
  • That's not what I am saying at all. I am saying that if I declare a static array of Tiles (therefore non dynamic and not on the heap) directly within WinMain, it works. However, if I declare a static array of Tiles (again, on the stack) as a member of the class Map, and then I declare a local instance of Map in WinMain, which means the class variable is on the stack and the Tile array member is also on the stack, then it doesn't work. Anyway, while debugging and watching the call stack, it says there is a segmentation fault in pthread. It looks like it is not even getting to any code. – Chris Wood Oct 08 '16 at 03:10
  • static variables are always on heap. You should not declare `Title` member data as static. – Barmak Shemirani Oct 08 '16 at 03:51
  • Not a static variable. A static array. Maybe I should use the term "fixed dimension." Maybe you guys are used to totally unintelligent brand new programmers, but I am not one of those. I've actually done a lot of programming. It's been a bit, but I've done game programming before and business programming for quite a while. I'm a little bit rusty, but quite intelligent. I know that dynamically allocated variables are on the heap. And actually, static variables are not specifically on the heap. They are stored in "data", which functions similarly to the heap, as are globals. – Chris Wood Oct 08 '16 at 14:27