Need help with Visual C++ memory leak

A place for people with an interest in developing new shmups.
Post Reply
atheistgod1999
Banned User
Posts: 1370
Joined: Sun May 03, 2015 6:21 pm
Location: Newton, MA, USA

Need help with Visual C++ memory leak

Post by atheistgod1999 »

A couple weeks ago, I got bored and threw together a (shitty) shmup in a couple of hours with the basic C++ knowledge I had in Visual Studio. There's nothing obviously wrong with my code, so it has to be a memory leak. I didn't follow any tutorial for this and the only ones I took beforehand were just doing stuff like really simple CYOAs (only googled how to sleep, clear the screen, and check keyboard input without hitting enter; nothing besides that).

I was going to ask at the time, but I forgot and just remembered now.

Even though it's a shitty shmup (and I made the playfield wider than tall despite being a vertical scroller), I still think it would be nice to know how to prevent memory leaks for the future.

Here's my code, by the way:
Spoiler

Code: Select all

#include <iostream>
#include <stdlib.h>
#include <conio.h>
#include <Windows.h>

using namespace std;


void printField(char pF[15][20]) {
	for (int y = 0; y < 15; y++) {
		for (int x = 0; x < 20; x++) {
			cout << pF[y][x] << ' ';
		}
		cout << endl;
	}
}

void fieldRefresh(char pF[15][20]) {
	for (int y = 0; y < 15; y++) {
		for (int x = 0; x < 20; x++) {
			pF[y][x] = ' ';
		}
	}
}

int main(void) {

	char playField[15][20];

	int playerPositionX = 9;
	int playerPositionY = 7;

	int enemyTick = 1;

	int score = 0;

	int scrollPosition = 0;

	bool bulletOnScreen[3] = { false,false,false };
	bool gameOver = false;

	int bulletPositionX[3];
	int bulletPositionY[3];

	int enemyPosition[60][6][3];

	int gameLength = 60;

	for (int i = 0; i < gameLength; i++) {
		for (int j = 0; j < 6; j++) {
			enemyPosition[i][j][0] = 0;
			enemyPosition[i][j][1] = 20;
			enemyPosition[i][j][2] = -1;
		}
	}

	//enemy positions

	enemyPosition[45][1][1] = 7;
	enemyPosition[45][0][1] = 12;
	enemyPosition[44][1][1] = 8;
	enemyPosition[44][0][1] = 11;
	enemyPosition[43][1][1] = 9;
	enemyPosition[43][0][1] = 10;
	enemyPosition[42][1][1] = 9;
	enemyPosition[42][0][1] = 10;
	enemyPosition[41][1][1] = 8;
	enemyPosition[41][0][1] = 11;
	enemyPosition[40][1][1] = 7;
	enemyPosition[40][0][1] = 12;

	enemyPosition[31][1][1] = 12;
	enemyPosition[31][0][1] = 15;
	enemyPosition[30][1][1] = 11;
	enemyPosition[30][0][1] = 14;
	enemyPosition[29][2][1] = 8;
	enemyPosition[29][0][1] = 13;
	enemyPosition[28][2][1] = 7;
	enemyPosition[28][0][1] = 12;
	enemyPosition[27][1][1] = 8;
	enemyPosition[27][0][1] = 11;
	enemyPosition[26][1][1] = 7;
	enemyPosition[26][0][1] = 10;
	enemyPosition[25][1][1] = 6;
	enemyPosition[25][0][1] = 9;
	enemyPosition[24][1][1] = 5;
	enemyPosition[24][0][1] = 8;
	enemyPosition[23][1][1] = 4;
	enemyPosition[23][0][1] = 7;
	enemyPosition[22][1][1] = 5;
	enemyPosition[22][0][1] = 8;
	enemyPosition[21][1][1] = 6;
	enemyPosition[21][0][1] = 9;
	enemyPosition[20][1][1] = 7;
	enemyPosition[20][0][1] = 10;
	enemyPosition[19][1][1] = 8;
	enemyPosition[19][0][1] = 11;
	enemyPosition[18][2][1] = 7;
	enemyPosition[18][1][1] = 9;
	enemyPosition[18][0][1] = 12;
	enemyPosition[17][2][1] = 8;
	enemyPosition[17][1][1] = 10;
	enemyPosition[17][0][1] = 13;
	enemyPosition[16][1][1] = 11;
	enemyPosition[16][0][1] = 14;
	enemyPosition[15][1][1] = 12;
	enemyPosition[15][0][1] = 15;

	enemyPosition[7][0][1] = 9;
	enemyPosition[6][0][1] = 9;
	enemyPosition[5][0][1] = 9;
	enemyPosition[4][0][1] = 9;
	enemyPosition[3][0][1] = 11; //            E
	enemyPosition[2][0][1] = 10; //           E
	enemyPosition[1][1][1] = 11;
	enemyPosition[1][0][1] = 9;  //          E E
	enemyPosition[0][0][1] = 10; //           E
	//enemy positions

	while (gameOver == false) {
		
		system("cls");

		fieldRefresh(playField);

		//check input
		if (_kbhit()) {
			char input = _getch();

			switch (input) {  
			case 'w':
				if (playerPositionY > 0) {
					playerPositionY -= 1;
				}
				break;
			case 's':
				if (playerPositionY < 14) {
					playerPositionY += 1;
				}
				break;
			case 'a':
				if (playerPositionX > 0) {
					playerPositionX -= 1;
				}
				break;
			case 'd':
				if (playerPositionX < 19) {
					playerPositionX += 1;
				}
				break;
			}
			if (input == 'j') {
				for (int i = 0; i < 3; i++) {
					if (bulletOnScreen[i] == false) {
						bulletOnScreen[i] = true;
						bulletPositionX[i] = playerPositionX;
						bulletPositionY[i] = playerPositionY - 1;
						break;
					}
				}
			}
		}
		//check input

		playField[playerPositionY][playerPositionX] = 'A';

		//bullet update
		for (int i = 0; i < 3; i++) { 
			if (bulletOnScreen[i]) {
				if (bulletPositionY[i] >= 0) {
					playField[bulletPositionY[i]][bulletPositionX[i]] = '*'; 
					bulletPositionY[i] -= 1;
				}
				else {
					bulletOnScreen[i] = false;
				}
			}
		}
		//bullet update

		//enemy update
		if (enemyTick == 0) {
			for (int i = 0; i < 6; i++) {
				if (enemyPosition[scrollPosition][i][1] >= 0 && enemyPosition[scrollPosition][i][1] < 20 && enemyPosition[scrollPosition][i][2] == -1) {
					enemyPosition[scrollPosition][i][0] = 1;
				}
			}

			for (int j = 0; j < gameLength; j++) {
				for (int k = 0; k < 6; k++) {
					if (enemyPosition[j][k][2] == 20) {
						enemyPosition[j][k][2] = -2;
						enemyPosition[j][k][0] = 0;
					}
					if (enemyPosition[j][k][0] == 1) {
						enemyPosition[j][k][2] += 1;
					}

				}
			}

			scrollPosition += 1;
		}

		for (int j = 0; j < gameLength; j++) {
			for (int k = 0; k < 6; k++) {
				if (enemyPosition[j][k][2] >= 0 && enemyPosition[j][k][2] < 20) {
					playField[enemyPosition[j][k][2]][enemyPosition[j][k][1]] = 'E';
				}
			}
		}

		//enemy update

		//collision check
		for (int j = 0; j < gameLength; j++) {
			for (int k = 0; k < 6; k++) {
				for (int b = 0; b < 3; b++) {
					if (enemyPosition[j][k][0] != 0) {
						if (enemyPosition[j][k][2] == bulletPositionY[b] && enemyPosition[j][k][1] == bulletPositionX[b] && bulletOnScreen[b]) {
							playField[enemyPosition[j][k][2]][enemyPosition[j][k][1]] = '%';
							//playField[bulletPositionY[b]][bulletPositionX[b]] = ' ';

							enemyPosition[j][k][2] = -2;
							enemyPosition[j][k][0] = 0;
							score = score + 1;
							bulletOnScreen[b] = false;
						}
					}
					if (enemyPosition[j][k][2] == playerPositionY && enemyPosition[j][k][1] == playerPositionX) {
						gameOver = true;
					}
				}
			}
		}

		printField(playField);

		enemyTick = enemyTick + 1;

		if (enemyTick == 5) {
			enemyTick = 0;
		}

		Sleep(1000 / 30);
		
		if (scrollPosition >= gameLength) {
			gameOver = true;
		}
	}
	system("cls");
	cout << "Game Over\nScore: " << score << endl;
	Sleep(1000);
}
I know I should first get rid of the functions and just copy/paste their code into where they're called in the program; just made them to make it more readable.
Xyga wrote:It's really awesome how quash never gets tired of hammering the same stupid shit over and over and you guys don't suspect for second that he's actually paid for this.
User avatar
waiwainl
Posts: 470
Joined: Tue Jun 24, 2014 9:23 am
Location: Netherlands

Re: Need help with Visual C++ memory leak

Post by waiwainl »

What is the actual problem you are having?
Visit: https://shmu.ps
Experience the National Video Game Museum in the Netherlands
atheistgod1999
Banned User
Posts: 1370
Joined: Sun May 03, 2015 6:21 pm
Location: Newton, MA, USA

Re: Need help with Visual C++ memory leak

Post by atheistgod1999 »

waiwainl wrote:What is the actual problem you are having?
It crashes seemingly-inconsistently. Like, it's not something that's obvious; it like slows down and stuff if too much stuff has happened.

At the end of the game, it's supposed to show the score, wait 2 seconds, and then allow the window to be closed by pressing any key. This works if I die early on, but if I die later on it instead crashes after the 2 seconds. I suggest you compile the code or something (or I should get a GitHub account and upload the .exe).
Xyga wrote:It's really awesome how quash never gets tired of hammering the same stupid shit over and over and you guys don't suspect for second that he's actually paid for this.
User avatar
Shepardus
Posts: 3505
Joined: Sat Dec 13, 2014 10:01 pm
Location: Ringing the bells of fortune

Re: Need help with Visual C++ memory leak

Post by Shepardus »

Does it give a specific error message when it crashes, like a segfault or something? I'd be surprised if it really were a memory leak, as at a glance I don't see any dynamic memory allocation being used.

Edit: Compiled it with some slight changes (Sleep to usleep) so I could compile it with MinGW. Might take a look at it tomorrow if I've got time, but if I were you the first thing I would check for is any possibility that you're accessing outside the boundaries of your arrays.

Edit 2: I stepped through the program with a debugger.
Spoiler
Just before the crash, playerPositionY got set to 17601 after executing line 208 (playField[enemyPosition[j][k][2]][enemyPosition[j][k][1]] = 'E'). The value of enemyPosition[j][k][2] when that happened was 18, which, since playField has only 15 rows, means this assignment went out of the bounds of the array and modified nearby memory allocated for playerPositionY. The segfault itself occurred at line 165 (playField[playerPositionY][playerPositionX] = 'A') since the program tried to access invalid memory with the corrupted value of playerPositionY.
Long story short, your program is segfaulting so you need to check all your array usages very carefully. I would especially focus on what values you're putting in enemyPosition and how that might cause out of bounds issues in places that read from enemyPosition, such as line 208. C++ is a tough choice of language for a beginner game programmer, and the lack of array bounds checking, as demonstrated here, is one of many reasons why. If you want to stick with C++ though I would get acquainted with some library that can handle windows, input, graphics, etc. SFML is one example but I'm sure others would have their own suggestions.
atheistgod1999 wrote:I know I should first get rid of the functions and just copy/paste their code into where they're called in the program; just made them to make it more readable.
No, you should do the opposite and split up that giant function into separate functions that are individually easy to reason with. Your goal as a programmer should be to make your code as easy to read and maintain as possible; all else is secondary.
Image
NTSC-J: You know STGs are in trouble when you have threads on how to introduce them to a wider audience and get more people playing followed by threads on how to get its hardcore fan base to play them, too.
1CCs | Twitch | YouTube
User avatar
Pixel_Outlaw
Posts: 2646
Joined: Sun Mar 26, 2006 3:27 am

Re: Need help with Visual C++ memory leak

Post by Pixel_Outlaw »

Enemy positions array seems to never be fully initialized.
Maybe my eyes are wrong green text kind of hurts em a bit.

You need to set all values in that array to 0 or other value.
You will need a for loop for each index, so a total of 3 nested for loops.
It's been a long time since I've used C++ you might check to see if there is a function to set all indices to 0 without the 3 nested "for" loops.

Also you want to avoid "magic" numbers. These are things that could be assigned then replaced by a variable.
If you have a fixed size array it is best to use a variable name to keep track rather than just saying 6. This way you just change the variable and all references update.

Be sure to set your compiler for C++11 or newer so you get the most out of the language.

If you're getting a lot of inconsistent errors chances are you're reaching into unassigned memory in C++.

As for a C++ game you might consider not binding yourself to windows and using something like Allegro and SFML so you can release for Mac and Linux with some modification.

The technique you're trying to do for the enemy positions is called "parallel arrays". It's probably a bit more desirable to simply store enemy positions as Enemy objects in a std::vector. Then you just iterate over that and update them. If you create a struct or class you just shove the instances in a std::vector<Enemy>. You can then prune out the dead by assigning a dead boolean attribute to enemies and looking for that.
Some of the best shmups don't actually end in a vowel.
No, this game is not Space Invaders.
Post Reply