user3025945 user3025945 - 2 months ago 5
C++ Question

C++ vector values keep changing?

This is a real simple problem. I'm writing a sliding block puzzle game for an exercise.

1, 1, 1, 1, 1,
1, 0, 3, 4, 1,
1, 0, 2, 2, 1,
1, 1, 1, 1, 1,


It receives input as in the form above, with '0' representing empty spaces, '1' representing walls, and all other numbers representing blocks.

Here is the class definition and constructor for the game state:

class GameState {

public:
GameState(int hght, int wdth);
GameState(const GameState &obj);
~GameState();
int getHeight();
int getWidth();
int getElem(int i, int j);
void setElem(int i, int j, int val);
void display();
void readFile(char* filename);
bool checkSolved();
map<int, vector<int*> > blockLocations;
vector<int> blockList;
void getBlockLocations();
void findBlock(int n);
private:
int **grid;
int height, width;
void allocate() {
grid = new int*[height];
for(int i = 0; i < height; i++)
{
grid[i] = new int[width];
}
}
};

GameState::GameState(int hght, int wdth) {
height = hght;
width = wdth;
allocate();
for(int i = 0; i < hght; i++) {
for (int j = 0; j < wdth; j++) {
grid[i][j] = 0;
}
}
};


Essentially, the grid is represented by a two-dimensional pointer array of integers.
height
and
width
are self-explanatory;
blockLocations
is a map that maps a block number to its point-wise coordinates of the form (y, x). For the time being, if a block occupies multiple spaces only the lowest rightmost space is listed. The matrix initializes as being nothing but zeros; the actual values are read in from a csv.

All of these methods are defined, but the two methods of concern are
getBlockLocations()
and
findBlock(int n)
.

void GameState::getBlockLocations() {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
blockList.push_back(grid[i][j]);
int pos[2] = {i, j};
vector<int*> v;
v.push_back(pos);
blockLocations[grid[i][j]] = v;
}
}
}

void GameState::findBlock(int n) {
vector<int>::iterator it;
it = find(blockList.begin(), blockList.end(), n);
if (it != blockList.end()) {
vector<int*> * posList = &blockLocations[n];
for (int itr = 0; itr < posList->size(); itr++) {
vector<int*> curPos = *posList;
cout << curPos[itr][0] << ", " << curPos[itr][1] << endl;
}
}
}


The problem comes up when I actually run this. As a case example, when I run
getBlockLocations()
, it correctly stores the coordinate for '2' as (2, 3). However, when I ask the program to display the location of that block with
findBlock(2)
, the resulting output is something along the lines of (16515320, 0). It's different every time but never correct. I don't see the pointer mistake I'm making to get incorrect values like this.

Answer

That is bad:

 for (int j = 0; j < width; j++) {
     blockList.push_back(grid[i][j]);
     int pos[2] = {i, j};
     vector<int*> v;
     v.push_back(pos);
     blockLocations[grid[i][j]] = v;
  }

You create a pos variable locally and store its reference. When you go out of scope of the for loop it is invalid / data can be replaced by something else.

(actually as Barmar pointed out, since the pos address is always the same within the loop, the values change at each iteration)

You could use a std::pair<int,int> to store your values instead. When you insert the pair in the vector, the data is copied, not only the pointer: it is safe.

typedef std::pair<int,int> IntIntPair;

IntIntPair pos(i,j);
std::vector<IntIntPair> v;