c++ 中的 free() 错误

Error in free() in c++

我认为与以下内容有关:如果您保存指向分配在堆栈上的对象的指针,该对象超出范围,然后您通过该指针修改某些内容,您可能会破坏其他人的内存。

我正在实现 RandomMaze 和 Pacman 游戏。 我有一张带有二维数组的地图(迷宫),我把走廊、墙壁、食物和吃豆人和幽灵的位置放在那里。在那张地图中,我知道吃豆人是否可以向东、向北等方向移动。 然后使用 opengl 我必须 "make the animation":它从屏幕中的 x,y 像素移动....

所以在主文件中我有一个名为 Maze m 的全局变量;然后我将它传递给 mypacman.moveEast(m,s); (屏幕具有屏幕的宽度和高度以绘制所有屏幕)。问题是(我想)我在操作中使用了 m,并且由于超出范围(我猜)它以某种方式调用了 free() 指令。

如有任何想法,我们将不胜感激..

vector<Ghost> playingGhosts;
Maze m;
Pacman myPacman;
Screen s ( WIDTH,HEIGHT);
Ghost Blinky, Pinky, Inky, Pokey;
int ghosts = 1;
long last_t=0;
int main (int argc, char *argv[])
{
    srand(time(NULL));
    m.setupMaze(ROWS, COLUMNS, LEVEL);
    m.printMaze();
    //myPacman.set_position(m.getRows(),0);

    /*Esta función es la que inicializa la GLUT y negocia con el sistema de ventanas para abrir una. Los parámetros deben ser los mismos argc y argv,
     * sin modificar, de la función main(). Además, Glut entiende una serie de parámetros que pueden ser pasados por línea de comandos.*/
    glutInit(&argc, argv);

    /*En esta ocasión, utilizamos GLUT_DOUBLE en vez de GLUT_SIMPLE. Esto hace posible la utilización de la técnica de “double buffer”, con la utilizamos
     * dos buffers para pintar en uno mientras se visualiza el otro. Con esto conseguimos una mayor fluidez en escenas.
     */
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

    //Posición x e y de la esquina superior izquierda de la nueva ventana, con respecto al escritorio en el que se trabaje.
    glutInitWindowPosition(WINDOW_X, WINDOW_Y);

    //Comprobamos que el ancho y alto de nuestra ventana es menor que los pixeles de la pantalla.
    //En ese caso no cabria nuestra ventana en la pantalla
    if ( glutGet(GLUT_SCREEN_WIDTH) == 0 || glutGet(GLUT_SCREEN_WIDTH) < WIDTH)
        throw out_of_range("Error: El ancho no puede ser cero. Error ancho pantalla");

    if (glutGet(GLUT_SCREEN_HEIGHT) == 0 || glutGet(GLUT_SCREEN_HEIGHT) < HEIGHT)
        throw std::out_of_range("Error: El alto no puede ser cero. Error alto pantalla");

    //El ancho y alto de la nueva ventana.
    glutInitWindowSize(WIDTH,HEIGHT);

    // Esta función es la que propiamente crea la ventana y el parámetro es el nombre de la misma.
    glutCreateWindow("Random Maze");



              //Ghost g;
            //  g= new Ghost();
              int x= m.getRows();
              int y =m.getColumns();

             // playingGhosts.push_back(5,7);// Esto esta en mapa hay que traducirlo a pantalla o al reves...insertar en pantalla directamente
              /*g.set_positionMap((x/2) - 1, (y/2) -1); // Blinky
              playingGhosts.push_back(g);

              g.set_positionMap((x/2) - 1, (y/2)); // Pinky
              playingGhosts.push_back(g);

              g.set_positionMap((x/2), (y/2)-1); // Inky
              playingGhosts.push_back(g);

              g.set_positionMap((x/2),(y/2)); // Pokey
              playingGhosts.push_back(g);*/


              //cout << "Numero de fantasmas en vector: " << playingGhosts.size() << endl;

    glutDisplayFunc(display);

    glutKeyboardFunc(keyboard);

    glutSpecialFunc(specialkeyboard);

    glutIdleFunc(idle);

    /*Especifica la matriz actual para realizar la composición. En ogl las operaciones de rotación, translación, escalado, etc. se
     * realizan a través de matrices de transformación. Dependiendo de lo que estemos tratando, hay tres tipos de matriz (que son
     * los tres posibles flags que puede llevar de parámetro la función): matriz de proyección (GL_PROJECTION),
     * matriz de modelo (GL_MODELVIEW) y matriz de textura (GL_TEXTURE). Con esta función indicamos a cual de estas tres deben
     * afectar las operaciones. Concretamente, GL_PROJECTION afecta a las vistas o perspectivas o proyecciones.
     */
    glMatrixMode(GL_PROJECTION);

              /*glOrtho() define una perspectiva ortonormal. Esto quiere decir que lo que se ves será una proyección paralela en uno de los
               * planos definidos por los ejes. Los parámetros sirven para delimitar el volumen de vista y son, por este orden:
               * x_mínima, x_máxima, y_mínima, y_máxima, z_mínima, z_máxima, (estos dos últimos no son coordenadas como los cuatro primeros,
               * son distancias desde el punto de vista, positivas hacia donde apunta y negativas hacia el lado contrario) considerando que,
               * por defecto, el punto de vista está en el origen de coordenadas mirando hacia el eje negativo de z, estos valores son
               * desplazamientos desde este punto. Con estos seis valores se define el volumen que incluirá los objetos que se proyecten.*/

              gluOrtho2D(0,WIDTH-1,0,HEIGHT-1);

              //Esta función cede el control del flujo del programa a GLUT que, a partir de estos "eventos", irá llamando a las funciones que han sido pasadas como callbacks.
              glutMainLoop();
              return 0;

}

//la función display() al ser pasada a glutDisplayFunc(), será llamada cada vez que haya que redibujar la ventana


void display()
{
    //int starti,startj;

    /*Con esto se define el color con el que se borrara el buffer al hacer un glClear().
     * Los 3 primeros parámetros son las componentes R, G y B, siguiendo un rango de [0..1]. La última es el valor alpha.
     */
    glClearColor(0.0,0.0,0.0,0.0);

    /* Borra un buffer o una combinación de varios. En este caso, borra el buffer de color (en realidad, cada componente
    R G y B tienen un buffer distinto, pero aquí los trata como el mismo), el que se pinta después en pantalla.
    Para borrarlos utiliza el color que ha sido previamente definido en init() mediante glClearColor(), en este caso,
    el (0,0,0,0) es decir, pinta todo el buffer de negro.*/
    glClear(GL_COLOR_BUFFER_BIT);

  m.drawMaze(WIDTH,HEIGHT);
  myPacman.draw();

  if(ghosts > 0)
  Blinky.draw(1.0,0.0,0.0);

  if(ghosts > 1)
  Pinky.draw(0.73,0.56,0.56);

  if(ghosts > 2)
  Inky.draw(0.0,1.0,1.0);

  if(ghosts > 3)
  Pokey.draw(1.0,0.5,0.0);

  //movimiento fantasmas, no sirve
  /*for(int i = 0; i < playingGhosts.size(); i++)
     {
      playingGhosts[i].draw();

      startj = playingGhosts[i].get_positionMapJ();
      starti = playingGhosts[i].get_positionMapI();

      cout << "Fantasma " << i << "Pos j: " << startj << endl;
      cout << "Fantasma " << i << "Pos i: " << starti << endl;

      m.moveGhost(starti,startj);


      startj = playingGhosts[i].get_positionMapJ();
          starti = playingGhosts[i].get_positionMapI();

          cout << "FantasmaPost " << i << "Pos j: " << startj << endl;
          cout << "FantasmaPost " << i << "Pos i: " << starti << endl;

     }*/





  //to-do comment
  glutSwapBuffers();

  //Le dice al procesador de eventos de GLUT que la ventana actual necesita ser redibujada
  //glutPostRedisplay();

}




/*-----------------------------------------------
//-----------------------------------------------
void display()
{
  glClearColor(0,0,0,0.0);
  glClear(GL_COLOR_BUFFER_BIT);

  myPacman.draw();

  glutSwapBuffers();
}
*/
//-----------------------------------------------
//-----------------------------------------------
void keyboard(unsigned char c,int x,int y)
{
  Ghost glocal;
  switch(c)
    {
    case 27: // ESC
          exit (0);
          break;
    case 43: // key '+' to add a ghost  //could be 65 (a) o 97(A)
        // Siempre insertamos en el mismo sitio, podemos mirar de cambiar el sitio. Nota posicion x e y en mapa.
        ghosts++;
         glocal.set_positionMap((x/2) - 1, (y/2) -1);
         playingGhosts.push_back(glocal);
         cout << "Añadimos fantasma. Numero de fantasmas en vector: " << playingGhosts.size() << endl;
         break;
    case 45: // key '-' to remove a ghost  //could be 81 (q) o 113 (Q)
            //Nos aseguramos de que siempre haya al menos un fantasma jugando
        if (ghosts > 1) ghosts--;
             if (playingGhosts.size() > 1) {
                 playingGhosts.pop_back();
                 cout << "Eliminamos fantasma. Numero de fantasmas en vector: " << playingGhosts.size() << endl;
             }
             else cout << "No podemos eliminar fantasma. Al menos tiene que haber un fantasma en juego.Numero de fantasmas en vector: " << playingGhosts.size() << endl;
            // playingGhosts.pop_back();
            break;

   }
  glutPostRedisplay();
}

void specialkeyboard(int key,int x,int y)
{
    /*
if ( myPacman.getState() == QUIET) // Creo que necesario si avanzamos d muchos en muchos...
{*/
    switch(key)
    {
    case GLUT_KEY_UP:
            goNorth();
            break;
    case GLUT_KEY_DOWN:
            goSouth();
            break;
    case GLUT_KEY_LEFT:
             myPacman.moveWest(m, s);
            //goWest();
            break;
    case GLUT_KEY_RIGHT:
            myPacman.moveEast(m, s);
            cout << "Maze esta bien: " << m.getRows() << endl;
            //goEast();
            break;
            //Falta igual un default

    //}
}
    //glutPostRedisplay();
  glutPostRedisplay();
}

我的 pacman class 实现:

#include "Pacman.h"




namespace RandomMaze {

Pacman::Pacman() {
    // TODO Auto-generated constructor stub

}

Pacman::~Pacman() {
    // TODO Auto-generated destructor stub
}

//-----------------------------------------------
void Pacman::moveEast(Maze m, Screen s)
{
                    int starti,startj, endi, endj;

                    int relacionAncho=int(s.getWidth() / m.getColumns());
                    int relacionAlto= int(s.getHeigth() / m.getRows());
                    int ysim;
                    cout << "relacionAnchoDentro : " << relacionAncho << endl;
                    cout << "relacionAltoDentro :  " << relacionAlto << endl;

                    starti = m.getPacmanX();
                    startj = m.getPacmanY();
                    cout << "Pacman posicion inicial i D: " << starti << endl;
                    cout << "Pacman posicion inicial j D: " << startj << endl;

                    ysim=0;
                    ysim=m.getRows();
                    cout << "PacmanX numero rows D: " << ysim << endl;
                    ysim= ysim-starti;
                    cout << "PacmanX ysim resultante: D" << ysim << endl;

                    set_positionScreen(startj*relacionAncho,ysim*relacionAlto);
                    cout << "Set positicion Screen Ancho D: " << startj*relacionAncho << " Alto: D" << ysim*relacionAlto << endl;

                    if ( m.isPassageOrFood( starti, startj+1)) // Si el map de arriba es visitable
                                { //actualizo posicion de pacman
                                m.deleteFood(starti,startj);
                                m.setPacman(starti,startj+1); // NO FOOD PORQUE YA HE PASADO
                                //myPacman.set_positionScreen(startj*relacionAncho,starti*relacionAlto);//  map[x][y-1]=PACMAN; //El sitio de arriba es pacman
                                    //myPacman.init_movement(x,y-1, 1000);
                                //m.drawMaze(WIDTH,HEIGHT);
                                startj++;
                                }


                    //colocacion pacman

                    /** Aqui hay algo mal calculado
                                         * La posiciones iniciales van al reves y no estan bien calculadaslo que va tan rapido que no lo vemos
                                         * el set position aqui no hace falta**/
                    /** PACMAN vota, seguro q hay alguna doble inicializacion de la posicion inicial */

                    endi=starti;
                                endj=startj;
                                cout << "Posicion final i: D" << endi << endl;
                                cout << "Posicion final j: D" << endj << endl;

                            //  set_positionScreen(startj*relacionAncho,ysim*relacionAlto);
                            //  cout << "Me voy a mover desde x: D" << startj*relacionAncho << endl;
                            //  cout << "Me voy a mover desde y: D" << starti*relacionAlto << endl;

                            //  ysim=m.getRows()-endi;
                                //myPacman.set_position(y*relacionAncho,ysim*relacionAlto);PACMAN_SPEED
                                init_movement(endj*relacionAncho,ysim*relacionAlto, 100); //RATIO hace votar a PACMAN
                                cout << "Me voy a mover hasta x: D" << endj*relacionAncho << endl;
                                cout << "Me voy a mover hasta y: D" << ysim*relacionAlto << endl;

}



//-----------------------------------------------
void Pacman::moveWest(Maze m, Screen s)
{
                    int starti,startj, endi, endj;

                    int relacionAncho=int(s.getWidth() / m.getColumns());
                    int relacionAlto= int(s.getHeigth() / m.getRows());
                    int ysim;
                    cout << "relacionAnchoDentro : " << relacionAncho << endl;
                    cout << "relacionAltoDentro :  " << relacionAlto << endl;

                    starti = m.getPacmanX();
                    startj = m.getPacmanY();
                    cout << "Pacman posicion inicial i D: " << starti << endl;
                    cout << "Pacman posicion inicial j D: " << startj << endl;

                    ysim=0;
                    ysim=m.getRows();
                    cout << "PacmanX numero rows D: " << ysim << endl;
                    ysim= ysim-starti;
                    cout << "PacmanX ysim resultante: D" << ysim << endl;

                    set_positionScreen(startj*relacionAncho,ysim*relacionAlto);
                    cout << "Set positicion Screen Ancho D: " << startj*relacionAncho << " Alto: D" << ysim*relacionAlto << endl;

                    if ( m.isPassageOrFood( starti, startj-1)) // Si el map de arriba es visitable
                                                        { //actualizo posicion de pacman
                                                        m.deleteFood(starti,startj);
                                                        m.setPacman(starti,startj-1); // NO FOOD PORQUE YA HE PASADO
                                                        //  map[x][y-1]=PACMAN; //El sitio de arriba es pacman
                                                            //myPacman.init_movement(x,y-1, 1000);
                                                        //myPacman.set_positionScreen(startj*relacionAncho,starti*relacionAlto);

                                                        //m.drawMaze(WIDTH,HEIGHT);
                                                        startj--;
                                                        }

                    //colocacion pacman

                    /** Aqui hay algo mal calculado
                                         * La posiciones iniciales van al reves y no estan bien calculadaslo que va tan rapido que no lo vemos
                                         * el set position aqui no hace falta**/
                    /** PACMAN vota, seguro q hay alguna doble inicializacion de la posicion inicial */

                    endi=starti;
                                endj=startj;
                                cout << "Posicion final i: D" << endi << endl;
                                cout << "Posicion final j: D" << endj << endl;

                            //  set_positionScreen(startj*relacionAncho,ysim*relacionAlto);
                            //  cout << "Me voy a mover desde x: D" << startj*relacionAncho << endl;
                            //  cout << "Me voy a mover desde y: D" << starti*relacionAlto << endl;

                            //  ysim=m.getRows()-endi;
                                //myPacman.set_position(y*relacionAncho,ysim*relacionAlto);PACMAN_SPEED
                                init_movement(endj*relacionAncho,ysim*relacionAlto, 100); //RATIO hace votar a PACMAN
                                cout << "Me voy a mover hasta x: D" << endj*relacionAncho << endl;
                                cout << "Me voy a mover hasta y: D" << ysim*relacionAlto << endl;

}

void Pacman::draw()
{

   glColor3f(1.0,1.0,0.0);
  //Draw Circle
            glBegin(GL_POLYGON);
                //Change the 6 to 12 to increase the steps (number of drawn points) for a smoother circle
                //Note that anything above 24 will have little affect on the circles appearance
                //Play with the numbers till you find the result you are looking for
                //Value 1.5 - Draws Triangle
                //Value 2 - Draws Square
                //Value 3 - Draws Hexagon
                //Value 4 - Draws Octagon
                //Value 5 - Draws Decagon
                //Notice the correlation between the value and the number of sides
                //The number of sides is always twice the value given this range
                for(double i = 0; i < 2 * PI; i += PI / 6) //<-- Change this Value
                    glVertex3f(cos(i) * RADIUS + x+7.5, sin(i) * RADIUS + y-10, 0.0);
            glEnd();
            //Draw Circle
}

void Pacman::drawPacman()
{
  glColor3f(1,1,1);
  glBegin(GL_QUADS);
  glVertex2i(x-6,y-6);
  glVertex2i(x+6,y-6);
  glVertex2i(x+6,y+6);
  glVertex2i(x-6,y+6);
  glEnd();
}

} /* namespace RandomMaze */

所以基本思想:如果我想往西走,我会查看地图(向左箭头):如果可以(是通道或食物),那么我会更新 mypacman 地图中的位置(使用 m.setPacman(i,j)) 并计算屏幕坐标和 init_movement 这些坐标(pacman 是从粒子继承的 class)。

void particle::init_movement(int destination_x,int destination_y,int duration)
{
  vx = (destination_x - x)/duration;
  vy = (destination_y - y)/duration;

  state=MOVE;
  time_remaining=duration;
}

当我第一次向东移动时屏幕变黑,第二次出现错误: `/home/eduardo/Desarrollo/adttrabajos/RandomMaze/Debug/RandomMaze' 中的错误:free():无效指针:0x00007f40dd418db8 ***

MoveWest 也是如此

我猜想当我在操作中更新位置或改变迷宫时,在我不知情的情况下启动了某种免费程序。

谢谢

PD:maze.h

#define WALL 0
#define PASSAGE 1
#define FOOD 2
#define VISITED 9
#define RATIO 0.4

//Personajes
#define PACMAN 10 //Comecocos, amarillo

#define BLINKY 11  //Fantasma Rojo ( cazador)
#define PINKY 12  //Fantasma Rosa( emboscador)
#define INKY  13 //Fantasma Azul, cian ( caprichoso)
#define POKEY  14  //Fantasma Naranja ( bobo)




namespace RandomMaze {

/** Esta clase genera y gestiona el laberinto del juego. */
class Maze {

    private:
        int previousBlinky = BLINKY;
        int previousPinky = PINKY;
        int previousInky = INKY;
        int previousPokey = POKEY;

        //Atributos publicos
    public:
        int **map;
        int rows, columns;
        int level;

    //Metodos privados
    private:
        void fillMaze();
        void addBorders();
        void centerWalls();
        void addWalls();
        void exploreMaze(int fila, int columna);
        void checkWalls(int fila, int columna);
        void checkWalls();
        bool isConnected();
        bool isCenter(int r, int c);
        void getaway();
        int isWall(int level);
        void deleteBrick(int i, int j);
        void addFood();
        void addPacman();
        void addBlinky();
        void addPinky();
        void addInky();
        void addPokey();

    //Metodos públicos
    public:
        Maze();
        ~Maze(void);
        Maze(int filas, int columnas, int level);
        double getN();
        void setupMaze(int filas, int columnas, int level);
        void moveGhost(int fila, int columna);
        void printMaze();
        void setRows(int filas);
        void setColumns(int columnas);
        void setLevel(int nivel);
        int getRows();
        int getColumns();
        int getLevel();
        int** getMaze();
        // Pacman
        int getPacmanX();
        int getPacmanY();
        void setPacman( int x,int j);

        //Blinky
        int getBlinkyX();
        int getBlinkyY();
        void setBlinky( int x,int j);
        void setPreviousBlinky(int x,int j);

        //Pinky
        int getPinkyX();
        int getPinkyY();
        void setPinky( int x,int j);
        void setPreviousPinky(int x,int j);

        //INKY
        int getInkyX();
        int getInkyY();
        void setInky( int x,int j);
        void setPreviousInky(int x,int j);

        //POKEY
        int getPokeyX();
        int getPokeyY();
        void setPokey( int x,int j);
        void setPreviousPokey(int x,int j);

        void drawMaze(int width, int height);
        bool isPassageOrFood( int x, int y);
        void deleteFood(int x,int y);

};


} /* namespace RandomMaze */
void Pacman::moveWest(Maze m, Screen s)

这是你的问题。

当您调用此函数时,将调用 MazeScreen 的复制构造函数。在函数 returns 之后,析构函数在创建的对象上被调用。默认自动生成的复制构造函数将简单地将所有成员复制到新对象(包括 int** 映射指针)。然后当它被销毁时,该指针被释放,但原始迷宫仍然持有对该指针的引用,下一次调用函数将发生双重释放错误。

您想要的是将引用或指针传递给 Maze(也可能是 Screen):

void Pacman::moveWest(Maze* m, Screen* s)

为避免此类错误,您至少应声明复制构造函数和赋值运算符。这意味着遵循规则 3,即如果 class 有一个析构函数,它也应该有一个复制构造函数和一个赋值运算符。还有一条规则 5 表示您还需要有移动变体。