我目前正在尝试用 C++ 构建一个包含几轮的简单游戏。不幸的是,出于某种原因,在整整三轮之后,我一直遇到内存访问冲突并且程序崩溃,我只是想不通为什么,所以非常感谢任何帮助。
仅供参考:代码看起来更像 C 而不是 C++,因为到目前为止我只接触过 C,而且我才刚刚开始学习 C++。下周我将不得不用类而不是结构编写相同的程序。
我认为问题可能在于我在每轮之后再次释放和动态分配的竞技场。
玩家和对手的结构数组在每场比赛开始时分配。
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <limits>
struct arena{
int game_map[5][5];
};
struct character{
int life_points = 5;
int relic_counter = 0;
int total_relics = 0;
int maps_cleared = 0;
int x = 0;
int y = 0;
};
struct opponent{
int last_field;
int x;
int y;
};
void flush_input();
bool input_game();
void check_next_field(struct arena* arena, struct character* player, char nex_field, bool* player_dead);
void input_movement(struct arena* arena, struct character* player, bool* player_dead);
void opponent_movement(struct arena* arena, struct opponent* opponent_array, int opponent_num, bool* player_dead);
void initialize_player(struct character* player);
void create_arena(struct arena*);
void spawn_opponent(struct arena*, struct opponent* opponent_array, int opponent_num);
int relic_counter(struct arena* arena);
void print_arena(struct arena*);
void update_arena_mask(struct arena* arena, char arena_mask[][5]);
void print_arena_mask(char arena_mask[][5]);
int main()
{
//allocates memory for a new player and initializes them
struct character* player = (struct character*)malloc(sizeof(struct character));
initialize_player(player);
struct opponent* opponent_array = (struct opponent*)malloc(10 * sizeof(struct opponent));
bool first_game = true;
bool play_again = true;
bool player_dead = false;
char arena_mask[5][5];
int relic_count_map = 0;
srand((unsigned)time(NULL));
while(play_again){
//if the player died reinitialize character
if(player_dead){
initialize_player(player);
player_dead = false;
}
//allocates memory for a new map/opponent
struct arena* arena = (struct arena*)malloc(sizeof(struct arena));
//initializes new map
//checks if it contains at least one relic
do{
create_arena(arena);
relic_count_map = relic_counter(arena);
} while(relic_count_map == 0);
//spawns opponents depending on the number of maps cleared
for(int i = 0; i < (player->maps_cleared + 1); i++){
spawn_opponent(arena, opponent_array, i);
}
update_arena_mask(arena, arena_mask);
print_arena(arena);
std::cout << std::endl;
//game instructions
if(first_game){
std::cout << "WELCOME TO OASIS CRAWLER!" << std::endl;
std::cout << "-------------------------" << std::endl;
std::cout << "The goal of this game is to make it through as many maps as possible before your characters dies." << std::endl;
std::cout << "You finish a map once you have collected all of its relics." << std::endl;
std::cout << "'P' = PLAYER, 'D' = DANGER, 'W' = WELL, '?' = RELIC/DANGER, '!' = OPPONENT, '.' = EMPTY FIELD" << "\n" << std::endl;
std::cout << "Map: " << (player->maps_cleared + 1) << std::endl;
print_arena_mask(arena_mask);
std::cout << "Life Points: " << player->life_points << " " << "Relicts Collected: " << player->relic_counter << "/" << relic_count_map << "\n" << std::endl;
}else{
std::cout << "Map: " << (player->maps_cleared + 1) << std::endl;
print_arena_mask(arena_mask);
std::cout << "Life Points: " << player->life_points << " " << "Relicts Collected: " << player->relic_counter << "/" << relic_count_map << "\n" << std::endl;
}
while(1){
input_movement(arena, player, &player_dead);
//checks if the player moved into an opponent
if(player_dead){
break;
}
//spawns opponents depending on the number of opponents cleared
for(int i = 0; i < (player->maps_cleared + 1); i++){
opponent_movement(arena, opponent_array, i, &player_dead);
}
//checks if an opponent moved into the player
if(player_dead){
break;
}
std::cout << "Map: " << (player->maps_cleared + 1) << "\n" << std::endl;
update_arena_mask(arena, arena_mask);
print_arena(arena);
std::cout << std::endl;
print_arena_mask(arena_mask);
//check if all relics have been collected
if(player->relic_counter == relic_count_map){
break;
}
//check life points
if(player->life_points <= 0){
player_dead = true;
break;
}
std::cout << "Life Points: " << player->life_points << " " << "Relicts Collected: " << player->relic_counter << "/" << relic_count_map << "\n" << std::endl;
}
first_game = false;
player->total_relics += player->relic_counter;
player->relic_counter = 0;
//checks if the player is dead, if not they are asked if they want to play another round
if(!player_dead){
std::cout << "\n" << "MAP CLEARED" << "\n" << std::endl;
//updates character
player->x = 0;
player->y = 0;
player->maps_cleared++;
//clears all the opponents
for(int i = 0; i < (player->maps_cleared + 1); i++){
arena->game_map[opponent_array[i].y][opponent_array[i].x] = opponent_array[i].last_field;
}
//print out maps cleared and relics found
std::cout << "Maps Cleared: " << player->maps_cleared << " " << "Total Relics Collected: " << player->total_relics << "\n" << std::endl;
play_again = input_game();
}
else{
std::cout << "GAME OVER" << "\n" << std::endl;
arena->game_map[player->y][player->x] = 6;
update_arena_mask(arena, arena_mask);
print_arena_mask(arena_mask);
std::cout << "Maps Cleared: " << player->maps_cleared << " " << "Total Relics Collected: " << player->total_relics << "\n" << std::endl;
play_again = input_game();
}
//prints out end stats if the player isnt dead and doesnt want to play again
if(!player_dead && !play_again){
std::cout << "GAME OVER" << "\n" << std::endl;
update_arena_mask(arena, arena_mask);
print_arena_mask(arena_mask);
std::cout << "Maps Cleared: " << player->maps_cleared << " " << "Total Relics Collected: " << player->total_relics << "\n" << std::endl;
}
//frees arena if player doesnt want to continue
if(!play_again){
free(player);
free(opponent_array);
free(arena);
}else{
//frees memory for arena
free(arena);
}
}
return 0;
}
// discard all input up to the next line break
void flush_input(){
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
//asks the user if they want to play again, validates input, and returns bool value
bool input_game(){
char input;
while(1){
std::cout << "Play again? (Y/N): ";
std::cin >> input;
input = tolower(input);
if(input == 'y'){
flush_input();
std::cout << std::endl;
return true;
}
else if(input == 'n'){
flush_input();
std::cout << std::endl;
return false;
}
else{
std::cout << "\nINVALID INPUT\n" << std::endl;
flush_input();
}
}
}
/* TODO: linked list insert here */
//checks the next field the player moves to and updates player points accordingly
void check_next_field(struct arena* arena, struct character* player, int next_field, bool* player_dead){
int damage;
switch(next_field){
//character finds danger field and has 1/6 chance to lose one life_point
case 1:
damage = (rand() % 6) + 1;
if(damage == 1){
player->life_points--;
}
break;
//character finds well and gains 1 life_point unless he is already at max health
case 2:
if(player->life_points != 5){
player->life_points++;
}
break;
//character finds relict and relict counter is updated
case 3:
player->relic_counter++;
break;
//character finds opponent and dies
case 5:
*player_dead = true;
break;
//character finds nothing
default:
break;
}
}
void input_movement(struct arena* arena, struct character* player, bool* player_dead){
char input;
int next_field;
bool valid_input = false;
//checks if the character movement fit in the arena array
while(!valid_input){
//checks if the player's input is valid
while(1){
std::cout << "Please enter your characters next move! (WASD): ";
std::cin >> input;
input = tolower(input);
if(input == 'w' || input == 's' || input == 'a' || input == 'd'){
flush_input();
break;
}
else{
std::cout << "\nINVALID INPUT" << "\n" << std::endl;
flush_input();
}
}
switch(input){
//character moves up
case 'w':
//checks if the field the player moves to is in bounds of the arena
if((player->y - 1) >= 0){
//clear the field the character is currently on
arena->game_map[player->y][player->x] = 0;
//checks the type of field the player moves
next_field = arena->game_map[player->y - 1][player->x];
//function updates player points depending on the type of field
check_next_field(arena, player, next_field, player_dead);
//changes the player position according to the user input
player->y--;
//verifies input
valid_input = true;
}
break;
//character moves down
case 's':
if((player->y + 1) < 5){
arena->game_map[player->y][player->x] = 0;
next_field = arena->game_map[player->y + 1][player->x];
check_next_field(arena, player, next_field, player_dead);
player->y++;
valid_input = true;
}
break;
//character moves down
case 'a':
if((player->x - 1) >= 0){
arena->game_map[player->y][player->x] = 0;
next_field = arena->game_map[player->y][player->x - 1];
check_next_field(arena, player, next_field, player_dead);
player->x--;
valid_input = true;
}
break;
//character moves down
case 'd':
if((player->x + 1) < 5){
arena->game_map[player->y][player->x] = 0;
next_field = arena->game_map[player->y][player->x + 1];
check_next_field(arena, player, next_field, player_dead);
player->x++;
valid_input = true;
}
break;
}
//checks if the player made a valid moves and updates the game_map
if(valid_input){
arena->game_map[player->y][player->x] = 4;
}
else{
std::cout << "\nOUT OF BOUNDS\n" << std::endl;
}
}
std::cout << std::endl;
}
void opponent_movement(struct arena* arena, struct opponent* opponent_array, int opponent_num, bool* player_dead){
int next_field;
int last_field = opponent_array[opponent_num].last_field;
bool valid_move = false;
while(!valid_move){
int random = (rand() % 4) + 1;
switch(random){
//opponent moves up
case 1:
//checks if the field the opponent moves to is in bounds of the arena
if((opponent_array[opponent_num].y - 1) >= 0){
//turns last field back to the field it was before the opponent moved on it
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = last_field;
//checks the next field
next_field = arena->game_map[opponent_array[opponent_num].y - 1][opponent_array[opponent_num].x];
//checks if the next field is the player
if(next_field == 4){
*player_dead = true;
//checks if the next field is another opponent
}else if(next_field == 5){
break;
//if it is any other field, the next field is saved
//then the opponent moves to the next field
}else{
opponent_array[opponent_num].last_field = next_field;
opponent_array[opponent_num].y--;
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = 5;
}
valid_move = true;
}
break;
//opponent moves down
case 2:
if((opponent_array[opponent_num].y + 1) < 5){
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = last_field;
next_field = arena->game_map[opponent_array[opponent_num].y + 1][opponent_array[opponent_num].x];
if(next_field == 4){
*player_dead = true;
}else if(next_field == 5){
break;
}else{
opponent_array[opponent_num].last_field = next_field;
opponent_array[opponent_num].y++;
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = 5;
}
valid_move = true;
}
break;
//opponent moves left
case 3:
if((opponent_array[opponent_num].x - 1) >= 0){
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = last_field;
next_field = arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x - 1];
if(next_field == 4){
*player_dead = true;
}else if(next_field == 5){
break;
}else{
opponent_array[opponent_num].last_field = next_field;
opponent_array[opponent_num].x--;
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = 5;
}
valid_move = true;
}
break;
//opponent moves right
case 4:
if((opponent_array[opponent_num].x + 1) < 5){
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = last_field;
next_field = arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x + 1];
if(next_field == 4){
*player_dead = true;
}else if(next_field == 5){
break;
}else{
opponent_array[opponent_num].last_field = next_field;
opponent_array[opponent_num].x++;
arena->game_map[opponent_array[opponent_num].y][opponent_array[opponent_num].x] = 5;
}
valid_move = true;
}
break;
}
}
}
//initializes a new character
void initialize_player(struct character* player){
player->life_points = 5;
player->maps_cleared = 0;
player->relic_counter = 0;
player->total_relics = 0;
player->x = 0;
player->y = 0;
}
//initializes the fields of the game_map with random numbers
void create_arena(struct arena* arena){
for(int row = 0; row < 5; row++){
for(int col = 0; col < 5; col++){
int field = (rand() % 10) + 1;
switch(field){
//empty field
case 1 ... 4:
arena->game_map[row][col] = 0;
break;
//danger field
case 5 ... 8:
arena->game_map[row][col] = 1;
break;
//well field
case 9:
arena->game_map[row][col] = 2;
break;
//relict field
case 10:
arena->game_map[row][col] = 3;
break;
}
}
}
//places character on the map
arena->game_map[0][0] = 4;
}
void spawn_opponent(struct arena* arena, struct opponent* opponent_array, int opponent_num){
while(1){
int x = rand() % 5;
int y = rand() % 5;
//checks if the randomly generated spawn is not the players spawn or another opponent
//updates the map
if(x != 0 && y != 0){
if(arena->game_map[y][x] != 5){
opponent_array[opponent_num].last_field = arena->game_map[y][x];
opponent_array[opponent_num].x = x;
opponent_array[opponent_num].y = y;
arena->game_map[y][x] = 5;
break;
}
}
}
}
//checks if the generated game_map contains at least one relict
int relic_counter(struct arena* arena){
int relic_counter = 0;
for(int row = 0; row < 5; row++){
for(int col = 0; col < 5; col++){
if(arena->game_map[row][col] == 3){
relic_counter++;
}
}
}
return relic_counter;
}
//update arena mask
void update_arena_mask(struct arena* arena, char arena_mask[][5]){
int hidden;
for(int row = 0; row < 5; row++){
for(int col = 0; col < 5; col++){
int field = arena->game_map[row][col];
switch(field){
//danger field, chance that it is hidden
case 1:
hidden = rand() % 2;
if(hidden == 0){
arena_mask[row][col] = 'D';
}
else{
arena_mask[row][col] = '?';
}
break;
//well
case 2:
arena_mask[row][col] = 'W';
break;
//relict
case 3:
arena_mask[row][col] = '?';
break;
//player
case 4:
arena_mask[row][col] = 'P';
break;
//opponent
case 5:
arena_mask[row][col] = '!';
break;
//player died
case 6:
arena_mask[row][col] = 'X';
break;
//empty field
default:
arena_mask[row][col] = '.';
break;
}
}
}
}
//prints arena
void print_arena(struct arena* arena){
for(int row = 0; row < 5; row++){
for(int col = 0; col < 5; col++){
std::cout << arena->game_map[row][col] << " ";
}
std::cout << std::endl;
}
}
//prints arena mask
void print_arena_mask(char arena_mask[][5]){
for(int row = 0; row < 5; row++){
std::cout << "\t";
for(int col = 0; col < 5; col++){
std::cout << " " << arena_mask[row][col] << " ";
}
std::cout << "\n" << std::endl;
}
}
您可能两次释放“竞技场”,这可能会导致双重释放:
/* FREE MEMORY ARENA */
//frees memory for arena
free(arena);
/* FREE MEMORY PLAYER; STRUCT ARRAY; ARENA */
//frees arena if player doesnt want to continue
if(!play_again){
free(player);
free(opponent_array);
free(arena);
}
更新:
你可能错误地分配了'opponent_array',看:
/* ALLOCATE MEMORY STRUCT ARRAY */
struct opponent *opponent_array = (struct opponent *)malloc(10 * sizeof(struct opponent *));
显然需要一个包含 10 个对手的数组,但将包含 10 个指针的数组分配给了对手。上帝保佑你,你有
struct opponent
12 字节大小,而 64 位系统中的指针(虚拟地址)可能是 8 字节长度。所以你期望分配 120 个字节但实际上分配了 80
我希望这段代码是
/* ALLOCATE MEMORY STRUCT ARRAY */
struct opponent *opponent_array = (struct opponent *)malloc(10 * sizeof(struct opponent));
看看