2010-09-12 11 views
5

Así que estoy haciendo un juego de serpientes con telepuertos y los ratones habituales. Tenía un bucle se ejecuta la siguiente manera:Mysterious heisenbug?

while(snake.alive() && miceEaten < micePerLevel) 
{ 
    displayInfo(lives, score, level, micePerLevel - miceEaten); 
    //some code 
    if(miceEaten()) 
    { 
     //update score... 
    } 
    //more stuff... 
} 

El problema con el código anterior era que displayInfo es llamado antes de la puntuación se actualiza, por lo que después de comer un ratón, el usuario tiene que esperar hasta que el bucle se ejecuta de nuevo a ver su puntaje actualizado Así que moví esa línea de código a la parte inferior de la función:

while(snake.alive() && miceEaten < micePerLevel) 
{ 
    //some code 
    if(miceEaten()) 
    { 
     //update score... 
    } 
    //more stuff... 
    displayInfo(lives, score, level, micePerLevel - miceEaten); 
} 

y los telepuertos dejan de funcionar! El programa se bloquea cada vez que la serpiente alcanza un telepuerto. Y displayInfo utiliza el siguiente código:

stringstream s; 
s << "LEVEL " << left << setw(12) << level << "LIVES: " << setw(12) << lives << "MICE LEFT: " << setw(12) << miceLeft 
    << "SCORE: " << setw(13) << score; 
printLine(0, s.str(), WHITEONBLUE); 

Dónde printLine sólo tiene un color_set, mvprintw y refresh(). Nada que ver con Teleports. Extraño.

así que fui a la función de serpiente, donde la serpiente obtiene su próxima ubicación de un telepuerto:

body.push_back(teleports[overlap(next)]->teleportFrom(dir)); //next is a Location object 

Dónde teleports[overlap(next)]->teleportFrom(dir) devuelve la ubicación de la serpiente es para ser teletransportado a. En un intento para ver por qué estaba fallando (quizás Teleport regresaba algún lugar fuera de la pantalla?), He añadido los siguientes 3 líneas antes de la línea anterior:

Location l = teleports[overlap(next)]->teleportFrom(dir); 
    mvprintw(1, 0, "(%i, %i)", l.x, l.y); 
    refresh(); 

Y el problema desaparece!

No solo eso, sino que TENGO que tener esas tres líneas. Si hago un comentario mvprintw(1, 0, "(%i, %i)", l.x, l.y);, o refresh();, o ambos, el programa se bloquea como antes al llegar a un telepuerto.

¿Alguna idea sobre qué podría estar causando este comportamiento?

ACTUALIZACIÓN: que intentó eliminar todas las advertencias (que en su mayoría eran advertencias sobre las comparaciones de firmado/números sin signo), pero sólo 1 permanece hasta el momento:

warning: reference to local variable 'other' returned 

Y el código:

Location& Location::operator = (Location other) 
{ 
    if(this == &other) 
     return other; 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

¿Qué debo hacer para corregir esta advertencia?

+2

Estás obteniendo un comportamiento indefinido en alguna parte. (Probablemente, está accediendo a algo que está fuera de los límites y arruinando la pila, los datos que lo rodean o cualquier otra cosa). Tiene que pasar por su código y verificar todos sus accesos. – GManNickG

+2

Tiene un comportamiento indefinido en otro lugar de su código. Compila tu código con todas las advertencias activadas. Y asegúrese de que no haya advertencias informadas (las advertencias suelen ser errores). Cómo activar las advertencias dependerá del compilador. Si eso no funciona, debe usar una herramienta que le informará sobre la corrupción de la memoria (también depende de la plataforma). –

+0

@Martin York - Intenté eliminar todas las advertencias, consulte mi actualización. Creo que esta advertencia puede ser la causa, pero necesito ayuda. ¡Gracias! – wrongusername

Respuesta

7

Construir su operador de asignación de esta manera:
Siempre debe devolver * esto (incluso si fueran iguales). Pero nunca lo harían ya que creaba una copia local (por lo que este no era su error).

Location& Location::operator = (Location const& other) 
{ 
    // Does it really matter if you assign to self? 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

La copia y el intercambio estándar parecían un poco exagerados para una clase tan simple.

PS. Debería corregir todas las advertencias (incluso si son tan simples como la falta de coincidencia sin firmar). Si no los arregla, se volverá inmune a su potencia y no detectará un problema real porque está rodeado de una advertencia de que está ignorando. Así que corríjalos todos (siempre enciendo la bandera que hace que el compilador trate todas las advertencias como errores para que el código no compile si hay advertencias).

La forma correcta de implementar el operador de asignación (o la forma más comúnmente aceptada). Es el uso de la copia y de intercambio idioma:

// notice the parameter is passed by value (i.e. a copy). 
// So the copy part is aromatically taken care of here. 
// So now you just need tom implement the swap() part of the idiom. 
Location& Location::operator = (Location other) 
{ 
    this->swap(other); 
    return *this; 
} 

void Location::swap(Location& other) 
{ 
    std::swap(x, other.x); 
    std::swap(y, other.y); 
} 
+0

Hehe, mi profesor escribió eso en clase :) Pero con lo que escribiste, hay 0 advertencias, y el código funciona bien. ¡Gracias! – wrongusername

+1

No siempre puede tratar todas las advertencias como errores. Con demasiada frecuencia, hay encabezados de sistema que necesita (especialmente los más oscuros) que siempre producen advertencias cuando se usan, a pesar de funcionar bien. El mejor modo de avanzar en tales situaciones es poner en cuarentena el uso del encabezado ofensivo en un solo archivo fuente para que al menos el resto del código pueda estar libre de alertas. –

+0

@wrongusername @Martin: La única vez que utiliza el modificador copiar y cambiar es cuando necesita escribir el Big Three. ¿'Location' realmente necesita un operador de asignación personalizado? (¡Como no está administrando nada, lo dudo!) – GManNickG

3

¿Ha comprobado el código que causa esta anomalía? Los Heisenbug fenómenos citados aquí:

Un ejemplo común es un error que se produce en un programa que fue compilado con un compilador de optimización, pero no en el mismo programa cuando se compila sin optimización (por ejemplo, para generar un modo de depuración versión)

Estas son algunas pautas: condición

  • Race? ¿Estás usando hilos?
  • ¿Borde del desbordamiento del puntero en alguna parte?
  • ejecutar su código a través valgrind para monitorear cualquier cambio inusual/erráticos en los buffers de memoria en algún lugar

Otra cita:

Una razón común para el comportamiento Heisenbug-como es que la ejecución de un programa de depuración modo a menudo limpia la memoria antes de que comience el programa, y ​​fuerza a las variables a la ubicación de la pila, en lugar de mantenerlas en registros. Estas diferencias en la ejecución pueden alterar el efecto de errores que involucran el acceso de miembros fuera de límites o suposiciones incorrectas sobre los contenidos iniciales de la memoria. Otra razón es que los depuradores suelen proporcionar relojes u otras interfaces de usuario que provocan que se ejecute código adicional (como los descriptores de acceso a propiedades), lo que a su vez puede cambiar el estado del programa. Otra razón más es un fandango en el núcleo, el efecto de un puntero que se está quedando sin límites. En C++, muchas heisenbugs son causadas por variables no inicializadas.

Asegúrese de que los interruptores están apagados - sin optimización, información completa de depuración, desactive cualquier construcciones existentes, reinicie el IDE y volver a compilar de nuevo ....

+2

+1 para valgrind. (Tómeme también). –

+0

Sin hilos: aún no aprendí a utilizarlos. Verificará valgrind, pero ¿cómo sabré si hay algún cambio inusual? ¿Y qué es un desbordamiento de puntero? He habilitado todas las advertencias en el compilador y no veo ningún cuadro de optimización marcado. ¡Gracias! – wrongusername

+0

@wrongusername: ¿qué compilador estás usando? – t0mm13b

4
Location& Location::operator = (Location other) 
{ 
    if(this == &other) 
     return other; 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

Esto devuelve una referencia. Cuando la función vuelve, ¿qué ocurre con other? (Muere y no te refieres a nada). Dado que esta es la clase con la que estás tratando en el área del problema, esta es probablemente la causa. La reorganización del código circundante deja la pila en una determinada condición en la que la referencia a la variable muerta "funciona".

Cámbielo a return *this, o simplemente elimine el cheque por completo. (Asignación de dos variables sin sucursal será probablemente siempre correr más rápido que la adición de una rama, en una CPU moderna.)

(Usted debe también, en general tomar el parámetro por referencia, en lugar de por valor.)

+1

La prueba 'this == & other' SIEMPRE será falsa, ya que' other' siempre será una temperatura asignada en la pila. Entonces, este código, aunque pobre, probablemente no sea el problema. Para arreglar la advertencia, saque la prueba y regrese pronto. –

+0

@Chris: Ah, buen punto. – GManNickG

+0

Entonces, el problema era que el operador de asignación del OP devolvía una referencia a la variable local (parámetro call by value). ¿Es eso correcto? – Chubsdad

1

En primer lugar, su ubicación :: = operador debe ser como este en su lugar:

Location& Location::operator = (const Location &other) 
{ 
    if(this == &other) 
     return *this; 
    x = other.x; 
    y = other.y; 
    return *this; 
} 

Sin embargo, eso probablemente no explica el accidente. Los indicadores incorrectos en la pila no se bloquean en la mayoría de las arquitecturas (suponiendo que xey son int).

Ahora bien, este es un mandelbug, no un heisenbug.Tienes a alguien más en algún lugar corrompiendo la memoria. Buena suerte.