C++ Battle4Zion 项目抛出未处理的异常:读取访问冲突。 **this** 是 nullptr。发生了
Posted
技术标签:
【中文标题】C++ Battle4Zion 项目抛出未处理的异常:读取访问冲突。 **this** 是 nullptr。发生了【英文标题】:C++ Battle4Zion project Unhandled exception thrown: read access violation. **this** was nullptr. occurred 【发布时间】:2017-11-08 03:54:37 【问题描述】:我已经启动了我的程序来测试我的头像在竞技场中的动作。我试图向任何方向移动我的头像,但我的程序停止工作。使用调试器,我在my Robot::move()
函数中发现了一条消息
抛出未处理的异常:读取访问冲突。 this 为 nullptr。发生了
我复制了以下代码,希望你们能解释我必须做什么:
// zion.cpp
// Portions you are to complete are marked with a TODO: comment.
// We've provided some incorrect return statements (so indicated) just
// to allow this skeleton program to compile and run, albeit incorrectly.
// The first thing you probably want to do is implement the trivial
// functions (marked TRIVIAL). Then get Arena::display going. That gives
// you more flexibility in the order you tackle the rest of the functionality.
// As you finish implementing each TODO: item, remove its TODO: comment.
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
///////////////////////////////////////////////////////////////////////////
// Manifest constants
///////////////////////////////////////////////////////////////////////////
const int MAXROWS = 20; // max number of rows in the arena
const int MAXCOLS = 40; // max number of columns in the arena
const int MAXROBOTS = 130; // max number of robots allowed
const int MAXSHOTLEN = 5; // max number of steps you can shoot
const int UP = 0;
const int DOWN = 1;
const int LEFT = 2;
const int RIGHT = 3;
///////////////////////////////////////////////////////////////////////////
// Auxiliary function declarations
///////////////////////////////////////////////////////////////////////////
int decodeDirection(char dir);
void clearScreen();
///////////////////////////////////////////////////////////////////////////
// Type definitions
///////////////////////////////////////////////////////////////////////////
class Arena; // This is needed to let the compiler know that Arena is a
// type name, since it's mentioned in the Robot declaration.
class Robot
public:
// Constructor
Robot(Arena* ap, int r, int c);
// Accessors
int row() const;
int col() const;
// Mutators
void move();
bool takeDamageAndLive();
private:
Arena* m_arena;
int m_row;
int m_col;
// TODO: You'll probably find that a robot object needs an additional
// data member to support your implementation of the behavior affected
// by being hit and taking damage.
;
class Player
public:
// Constructor
Player(Arena *ap, int r, int c);
// Accessors
int row() const;
int col() const;
int age() const;
bool isDead() const;
// Mutators
string takeComputerChosenTurn();
void stand();
void move(int dir);
bool shoot(int dir);
void setDead();
private:
Arena* m_arena;
int m_row;
int m_col;
int m_age;
bool m_dead;
;
class Arena
public:
// Constructor/destructor
Arena(int nRows, int nCols);
~Arena();
// Accessors
int rows() const;
int cols() const;
Player* player() const;
int robotCount() const;
int nRobotsAt(int r, int c) const;
void display(string msg) const;
// Mutators
bool addRobot(int r, int c);
bool addPlayer(int r, int c);
void damageRobotAt(int r, int c);
bool moveRobots();
private:
int m_rows;
int m_cols;
Player* m_player;
Robot* m_robots[MAXROBOTS];
int m_nRobots;
;
class Game
public:
// Constructor/destructor
Game(int rows, int cols, int nRobots);
~Game();
// Mutators
void play();
private:
Arena* m_arena;
;
///////////////////////////////////////////////////////////////////////////
// Robot implementation
///////////////////////////////////////////////////////////////////////////
Robot::Robot(Arena* ap, int r, int c)
if (ap == nullptr)
cout << "***** A robot must be in some Arena!" << endl;
exit(1);
if (r < 1 || r > ap->rows() || c < 1 || c > ap->cols())
cout << "***** Robot created with invalid coordinates (" << r << ","
<< c << ")!" << endl;
exit(1);
m_arena = ap;
m_row = r;
m_col = c;
int Robot::row() const
return m_row;
int Robot::col() const
return m_col; // returns what column the robot is at.
void Robot::move()
// Attempt to move in a random direction; if we can't move, don't move
switch (rand() % 4)
case UP:
if (m_row > 1)
m_row--; /* Robot may move to the row above unless it is a
boundry. */
break;
case DOWN:
if (m_row < m_arena->rows())
m_row++; /* Robot may move to the row below unless it is a
boundry. */
break;
case LEFT:
if (m_col > 1)
m_col--; /* Robot may move to the left column unless it is a
boundry. */
break;
case RIGHT:
if (m_row < m_arena->cols())
m_col++; /* Robot may move to the right column unless it is a
boundry. */
break;
bool Robot::takeDamageAndLive()
// TODO: If the robot has been hit once before, return false (since a
// second hit kills a robot). Otherwise, return true (since the robot
// survived the damage).
return false; //temp
///////////////////////////////////////////////////////////////////////////
// Player implementations
///////////////////////////////////////////////////////////////////////////
Player::Player(Arena* ap, int r, int c)
if (ap == nullptr)
cout << "***** The player must be in some Arena!" << endl;
exit(1);
if (r < 1 || r > ap->rows() || c < 1 || c > ap->cols())
cout << "**** Player created with invalid coordinates (" << r
<< "," << c << ")!" << endl;
exit(1);
m_arena = ap;
m_row = r;
m_col = c;
m_age = 0;
m_dead = false;
int Player::row() const
return m_row;
int Player::col() const
return m_col;
int Player::age() const
return m_age;
string Player::takeComputerChosenTurn()
// Your replacement implementation should do something intelligent
// and return a string that describes what happened. When you've
// decided what action to take, take it by calling move, shoot, or stand.
// This function must return one of the following four strings:
// "Moved."
// "Shot and hit!"
// "Shot and missed!"
// "Stood."
// Here's one possible strategy:
// If moving in some direction would put me in less immediate danger
// than standing, then move in that direction.
// else shoot in the direction of the nearest robot I can hit.
// A more aggressive strategy is possible, where you hunt down robots.
int r = row(), c = col();
int nRobotsLeft = 0, nRobotsRight = 0, nRobotsUp = 0, nRobotsDown = 0;
if (c > 1) // checks the number of robots 1 space to the left of the player.
nRobotsLeft = m_arena->nRobotsAt(r, c - 1);
if (c < m_arena->cols()) /* checks the number of robots 1 space to the right
of the player. */
nRobotsRight = m_arena->nRobotsAt(r, c + 1);
if (r > 1) // checks the number of robots 1 space above the player
nRobotsUp = m_arena->nRobotsAt(r - 1, c);
if (r < m_arena->rows()) /* checks the number of robots 1 space under the
player. */
nRobotsDown = m_arena->nRobotsAt(r + 1, c);
int nRobotsLeftRow = 0, nRobotsRightRow = 0, nRobotsAboveCol = 0,
nRobotsBelowCol = 0;
for (int q = c - 2; q >= 1; q--) /* looking for robots on the same row to
the left, but not adjacent to the player */
nRobotsLeftRow += m_arena->nRobotsAt(r, q); /* increments the number of
robots if any are found */
for (int q = c + 2; q <= m_arena->cols(); q++) /* looking for robots on
the same row, to the right, but not adjacent to the player */
nRobotsRightRow += m_arena->nRobotsAt(r, q); /* increments the number of
robots if any are found */
for (int k = r - 2; k >= 1; k--) /* looks for robots in the same column,
above but not adjacent to the player */
nRobotsAboveCol += m_arena->nRobotsAt(k, c);
for (int k = r + 2; k <= m_arena->rows(); k++) /* looks for robots in the
same column, below but not adjacent to the player */
nRobotsBelowCol += m_arena->nRobotsAt(k, c);
// # robots directly next to player
int nRobotsAdjacent = nRobotsRight + nRobotsLeft + nRobotsUp + nRobotsDown;
int nRobotsInRange = nRobotsRightRow + nRobotsLeftRow + nRobotsAboveCol +
nRobotsBelowCol; //# robots not directly next to player but in shooting range
if (nRobotsAdjacent == 0) /* if no robots are adjacent to the player, */
if (nRobotsInRange > 0) /* and there are robots in shooting range, the player
should shoot towards a robot. */
if (nRobotsRightRow > 0) /*shoots right*/
bool hit = shoot(RIGHT);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
else if (nRobotsLeftRow > 0) /*shoots left*/
bool hit = shoot(LEFT);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
else if (nRobotsAboveCol > 0) /*shoots up*/
bool hit = shoot(UP);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
else if (nRobotsBelowCol > 0) /*shoots down*/
bool hit = shoot(DOWN);
if (hit)
return "Shot and hit!";
else
return "Shot and missed!";
else /* this should never trigger. only goes off if an error occurs. */
stand();
return "Stood.";
else /* player is safe, but has nothing to shoot at */
stand();
return "Stood.";
if (nRobotsAdjacent > 0)
/* should calculate the safest of the 4 spots and choose a spot to move to based on relative safety */
int safetyRatingR = m_arena->nRobotsAt(r + 1, c + 1) + m_arena->nRobotsAt(r - 1, c + 1) + m_arena->nRobotsAt(r, c + 2);
// creates an integer value of the amount of robots directly adjacent to the space 1 spot to the right of the player
int safetyRatingL = m_arena->nRobotsAt(r + 1, c - 1) + m_arena->nRobotsAt(r - 1, c - 1) + m_arena->nRobotsAt(r, c - 2);
// safety value for the left space
int safetyRatingU = m_arena->nRobotsAt(r - 1, c - 1) + m_arena->nRobotsAt(r - 1, c + 1) + m_arena->nRobotsAt(r - 2, c);
//safety value for the above space
int safetyRatingD = m_arena->nRobotsAt(r + 1, c + 1) + m_arena->nRobotsAt(r + 1, c - 1) + m_arena->nRobotsAt(r + 2, c);
//safety value for the below space
if (safetyRatingR <= safetyRatingL && safetyRatingR <= safetyRatingU && safetyRatingR <= safetyRatingD && c != m_arena->cols())
// if R has the lowest safety rating, and moving right would not take you off the grid
move(RIGHT);
return "Moved.";
else if ((safetyRatingL <= safetyRatingU) && (safetyRatingL <= safetyRatingD) && (c != 1))
// if L has the lowest safety rating of the remaining possible directions to move, and moving left would not go off the grid
move(LEFT);
return "Moved.";
else if ((safetyRatingU <= safetyRatingD) && (r != 1))
// if U has the lowest safety rating of the 2 remaining poss directions, and moving up would not go off the grid
move(UP);
return "Moved.";
else if (r != m_arena->rows()) // final option is to move down, only triggers if this would not take you off the grid
move(DOWN);
return "Moved.";
else // something went wrong, and none of the 4 directions will work
stand();
return "Stood.";
void Player::stand()
m_age++;
void Player::move(int dir)
m_age++;
switch (dir)
case UP:
if (m_row > 1)
m_row--;
break;
case DOWN:
if (m_row < m_arena->rows())
m_row++;
break;
case LEFT:
if (m_col > 1)
m_col--;
break;
case RIGHT:
if (m_col < m_arena->cols())
m_col++;
break;
bool Player::shoot(int dir)
m_age++;
if (rand() % 3 == 0) // miss with 1/3 probability
return false;
// Damages the nearest robot in direction dir, returning
// true if a robot is hit and damaged, false if not hit.
switch (dir)
case UP:
for (int r = m_row; r >= 1; r--) /* starts at the player's row position
and steps down, stopping at the end of the arena */
if (m_arena->nRobotsAt(r, m_col) >= 1) /* if there is a robot at the
current step, throw damage and return */
m_arena->damageRobotAt(r, m_col);
return true;
break;
case DOWN:
for (int r = m_row; r <= m_arena->rows(); r++) /* starts at player's row
and goes down */
if (m_arena->nRobotsAt(r, m_col) >= 1) /* if there is a robot at the
current step, throw damage and return */
m_arena->damageRobotAt(r, m_col);
return true;
break;
case LEFT:
for (int c = m_col; c >= 1; c--) /* starts at player's column position
and steps to the left one space at a time */
if (m_arena->nRobotsAt(m_row, c) >= 1)
m_arena->damageRobotAt(m_row, c); /* if there is a robot at the
current step, throw damage and return */
return true;
break;
case RIGHT:
for (int c = m_col; c <= m_arena->cols(); c++) /* starts at player's column
position and steps to the right one space at a time */
if (m_arena->nRobotsAt(m_row, c) >= 1)
m_arena->damageRobotAt(m_row, c); /* if there is a robot at the
current step, throw damage and return */
return true;
break;
return false; // didn't hit anything
bool Player::isDead() const
// returns whether the player is dead.
if (m_dead)
return true;
return false;
void Player::setDead()
m_dead = true;
///////////////////////////////////////////////////////////////////////////
// Arena implementations
///////////////////////////////////////////////////////////////////////////
Arena::Arena(int nRows, int nCols)
if (nRows <= 0 || nCols <= 0 || nRows > MAXROWS || nCols > MAXCOLS)
cout << "***** Arena created with invalid size " << nRows << " by "
<< nCols << "!" << endl;
exit(1);
m_rows = nRows;
m_cols = nCols;
m_player = nullptr;
m_nRobots = 0;
Arena::~Arena()
for (int i = 0; i < m_nRobots; i++)
delete m_robots[i]; /* deletes all remaining dynamically allocated
robots */
m_nRobots--;
delete m_player; // deletes the player
int Arena::rows() const
// TODO: TRIVIAL: Return the number of rows in the arena.
// Delete the following line and replace it with the correct code.
return m_rows; // This implementation compiles, but is incorrect.
int Arena::cols() const
// TODO: TRIVIAL: Return the number of columns in the arena.
// Delete the following line and replace it with the correct code.
return m_cols; // This implementation compiles, but is incorrect.
Player* Arena::player() const
return m_player;
int Arena::robotCount() const
return m_nRobots;
int Arena::nRobotsAt(int r, int c) const
// returns the number of robots at row r, column c.
int tally = 0;
for (int j = 0; j < m_nRobots; j++)
if (m_robots[j]->row() == r && m_robots[j]->col() == c)
tally++; /* adds a robot to the tally if it is positioned at
(r, c) */
return tally;
void Arena::display(string msg) const
// Position (row,col) in the arena coordinate system is represented in
// the array element grid[row-1][col-1]
char grid[MAXROWS][MAXCOLS];
int r, c;
// Fill the grid with dots
for (r = 0; r < rows(); r++)
for (c = 0; c < cols(); c++)
grid[r][c] = '.';
// Indicate each robot's position
/*for (r = 1; r <= rows(); r++)
for (c = 1; c <= cols(); c++)
if (nRobotsAt(r, c) == 1)
grid[r][c] = 'R'; /* If one robot is at some grid point,
char is set to 'R'. */
/*else if (nRobotsAt(r, c) > 1 && nRobotsAt(r, c) < 9)
grid[r][c] = nRobotsAt(r, c); /* If it's 2 though 8, char
is set to '2' through '8'.
else if (nRobotsAt(r, c) >= 9)
grid[r][c] = '9'; // For 9 or more, char is set to '9'.
else
grid[r][c] = '.';
*/
// Indicate player's position
if (m_player != nullptr)
// Set the char to '@', unless there's also a robot there,
// in which case set it to '*'.
char& gridChar = grid[m_player->row() - 1][m_player->col() - 1];
if (gridChar == '.')
gridChar = '@';
else
gridChar = '*';
// Draw the grid
clearScreen();
for (r = 0; r < rows(); r++)
for (c = 0; c < cols(); c++)
cout << grid[r][c];
cout << endl;
cout << endl;
// Write message, robot, and player info
cout << endl;
if (msg != "")
cout << msg << endl;
cout << "There are " << robotCount() << " robots remaining." << endl;
if (m_player == nullptr)
cout << "There is no player." << endl;
else
if (m_player->age() > 0)
cout << "The player has lasted " << m_player->age() << " steps." << endl;
if (m_player->isDead())
cout << "The player is dead." << endl;
bool Arena::addRobot(int r, int c)
// If the maximum amount of robots exists, none shall be added.
if (m_nRobots = MAXROBOTS)
return false;
// Dynamically allocates another robot and add it to arena
m_robots[m_nRobots] = new Robot(this, r, c);
m_nRobots++;
return true;
bool Arena::addPlayer(int r, int c)
// Don't add a player if one already exists
if (m_player != nullptr)
return false;
// Dynamically allocate a new Player and add it to the arena
m_player = new Player(this, r, c);
return true;
void Arena::damageRobotAt(int r, int c)
// TODO: Damage one robot at row r, column c if at least one is there.
// If the robot does not survive the damage, destroy it.
if (nRobotsAt(r, c) > 0)
for (int l = 0; l < m_nRobots; l++)
/* the following statement looks through the robot array to
find an element that has coordinates matching the robot taking
damage */
if (m_robots[l]->row() == r && m_robots[l]->col() == c)
// TODO: robot takes damage
delete m_robots[l]; // removes this element
while (l < m_nRobots)
m_robots[l] = m_robots[l + 1]; /* copies all elements
over one element */
l++;
m_nRobots--; // decrements the number of all robots
bool Arena::moveRobots()
for (int k = 0; k < m_nRobots; k++)
m_robots[k]->move(); // robot makes a move
/* If that move results in that robot being in the same
position as the player, the player dies. */
if (m_robots[k]->row() == m_player->row() && m_robots[k]->col() == m_player->col())
m_player->setDead();
// return true if the player is still alive, false otherwise
return !m_player->isDead();
///////////////////////////////////////////////////////////////////////////
// Game implementations
///////////////////////////////////////////////////////////////////////////
Game::Game(int rows, int cols, int nRobots)
if (nRobots > MAXROBOTS)
cout << "***** Trying to create Game with " << nRobots
<< " robots; only " << MAXROBOTS << " are allowed!" << endl;
exit(1);
// Create arena
m_arena = new Arena(rows, cols);
// Add player
int rPlayer = 1 + rand() % rows;
int cPlayer = 1 + rand() % cols;
m_arena->addPlayer(rPlayer, cPlayer);
// Populate with robots
while (nRobots > 0)
int r = 1 + rand() % rows;
int c = 1 + rand() % cols;
// Don't put a robot where the player is
if (r == rPlayer && c == cPlayer)
continue;
m_arena->addRobot(r, c);
nRobots--;
Game::~Game()
delete m_arena;
void Game::play()
Player* p = m_arena->player();
if (p == nullptr)
m_arena->display("");
return;
string msg = "";
do
m_arena->display(msg);
msg = "";
cout << endl;
cout << "Move (u/d/l/r/su/sd/sl/sr/c//q): ";
string action;
getline(cin, action);
if (action.size() == 0)
p->stand();
else
switch (action[0])
default: // if bad move, nobody moves
cout << '\a' << endl; // beep
continue;
case 'q':
return;
case 'c': // computer moves player
msg = p->takeComputerChosenTurn();
break;
case 'u':
case 'd':
case 'l':
case 'r':
p->move(decodeDirection(action[0]));
break;
case 's':
if (action.size() < 2) // if no direction, nobody moves
cout << '\a' << endl; // beep
continue;
switch (action[1])
default: // if bad direction, nobody moves
cout << '\a' << endl; // beep
continue;
case 'u':
case 'd':
case 'l':
case 'r':
if (p->shoot(decodeDirection(action[1])))
msg = "Hit!";
else
msg = "Missed!";
break;
break;
m_arena->moveRobots();
while (!m_arena->player()->isDead() && m_arena->robotCount() > 0);
m_arena->display(msg);
///////////////////////////////////////////////////////////////////////////
// Auxiliary function implementations
///////////////////////////////////////////////////////////////////////////
int decodeDirection(char dir)
switch (dir)
case 'u': return UP;
case 'd': return DOWN;
case 'l': return LEFT;
case 'r': return RIGHT;
return -1; // bad argument passed in!
///////////////////////////////////////////////////////////////////////////
// main()
///////////////////////////////////////////////////////////////////////////
int main()
// Initialize the random number generator. (You don't need to
// understand how this works.)
srand(static_cast<unsigned int>(time(0)));
// Create a game
// Use this instead to create a mini-game: Game g(3, 3, 2);
Game g(15, 18, 80);
// Play the game
g.play();
///////////////////////////////////////////////////////////////////////////
// clearScreen implementations
///////////////////////////////////////////////////////////////////////////
// DO NOT MODIFY OR REMOVE ANY CODE BETWEEN HERE AND THE END OF THE FILE!!!
// THE CODE IS SUITABLE FOR VISUAL C++, XCODE, AND g++ UNDER LINUX.
// Note to Xcode users: clearScreen() will just write a newline instead
// of clearing the window if you launch your program from within Xcode.
// That's acceptable.
#ifdef _MSC_VER // Microsoft Visual C++
#include <windows.h>
void clearScreen()
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(hConsole, &csbi);
DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
COORD upperLeft = 0, 0 ;
DWORD dwCharsWritten;
FillConsoleOutputCharacter(hConsole, TCHAR(' '), dwConSize, upperLeft,
&dwCharsWritten);
SetConsoleCursorPosition(hConsole, upperLeft);
#else // not Microsoft Visual C++, so assume UNIX interface
#include <cstring>
void clearScreen() // will just write a newline in an Xcode output window
static const char* term = getenv("TERM");
if (term == nullptr || strcmp(term, "dumb") == 0)
cout << endl;
else
static const char* ESC_SEQ = "\x1B["; // ANSI Terminal esc seq: ESC [
cout << ESC_SEQ << "2J" << ESC_SEQ << "H" << flush;
#endif
/*main.cpp
Open with*/
【问题讨论】:
请提供Minimal, Complete, and Verifiable example 请edit您的代码将其减少为您的问题的minimal reproducible example。您当前的代码包含许多与您的问题无关的内容 - 一个最小样本通常看起来类似于一个好的单元测试:只执行一项任务,并为可重复性指定输入值。 请edit你的问题告诉我们你做了什么样的调试。我希望您已经在 Valgrind 或类似的检查器中运行了您的minimal reproducible example,并使用诸如 GDB 之类的调试器进行了调查。确保您也启用了全套编译器警告。这些工具告诉了你什么,它们缺少什么信息?并阅读 Eric Lippert 的 How to debug small programs。 【参考方案1】:您的代码假定数组的索引从 1 到 N(例如,r < 1 || r > ap->rows()
),但在 C 和 C++ 中,数组从 0 到 N-1。访问位置 N 的数组是访问冲突。
【讨论】:
【参考方案2】:在addRobot
中,您的if
语句中有一个赋值,而不是比较。这导致不添加任何机器人,并最终取消引用 NULL 指针。
此外,您在Arena::~Arena
中泄漏了内存,因为您在循环中更改了m_nRobots
的值,导致一些机器人未被删除。
【讨论】:
以上是关于C++ Battle4Zion 项目抛出未处理的异常:读取访问冲突。 **this** 是 nullptr。发生了的主要内容,如果未能解决你的问题,请参考以下文章
从 Parallel.ForEach 抛出未处理的 OperationCanceledException