Laboratorio de Programación 2

Preguntas frecuentes


¿Cómo compilo un programa C++?

La forma más aconsejable de compilar un programa C++ con gcc es:


       g++ -Wall -Wextra -ansi -pedantic -o fichero.out fichero.cpp

Los parámetros -Wall -Wextra -ansi -pedantic pueden ayudarte a descubrir más errores en tus programas.
Compila el siguiente programa con y sin esos parámetros y observa la diferencia:


      #include <iostream>

      using namespace std;

      int main()
      {
         int x,y;

	 x = 1;
	 y = 2;

	 if (y=x)   // error, debería ser ==
	 {
            cout << "iguales" << endl;
	 }
	 else
         {
            cout << "distintos" << endl;
         }

	 return 0;
      }

¿Cómo uso Valgrind para detectar errores de punteros?

Valgrind es una herramienta que permite detectar errores de punteros en los programas C++. Para usar Valgrind es necesario modificar tanto la forma de compilar como la forma de ejecutar los programas.

Para emplear Valgrind debes pasar al compilador g++ dos parámetros adicionales: -g y -O0 (letra O seguida del dígito 0).


       g++ -Wall -Wextra -ansi -pedantic -o fichero.out -g -O0 fichero.cpp

Si olvidas añadir alguno de esos parámetros (especialmente -g) Valgrind no funcionará de la forma que aquí se describe.

Una vez compilado el programa, la ejecución debe hacerse desde Valgrind:


       valgrind --leak-check=yes  fichero.out

Valgrind mostrará un mensaje de error cada vez que detecte un error de punteros. Por ejemplo:


      1)  ==23848== Invalid write of size 4
      2)  ==23848==    at 0x8048B07: main (errores_comunes.cpp:168)
      3)  ==23848==  Address 0x1B936028 is 0 bytes inside a block of size 4 free'd
      4)  ==23848==    at 0x1B9098CF: operator delete(void*) (vg_replace_malloc.c:155)
      5)  ==23848==    by 0x8048ACB: main (errores_comunes.cpp:165)

De un mensaje de error generalmente nos interesará:

Los mensajes de error de Valgrind son bastante detallados y alguna de la información que proporcionan puede resultar confusa para un principiante. Ten en cuenta que los mensajes de error sobre memoria no inicializada pueden ser bastante profusos. Por ejemplo:


      ==19427== Use of uninitialised value of size 4
      ==19427==    at 0x41428C2C: (within /usr/lib/libstdc++.so.6.0.4)
      ==19427==    by 0x4143193C: std::ostreambuf_iterator > ...
      ==19427==    by 0x41431B4F: std::num_put ...
      ==19427==    by 0x414422C3: std::ostream::operator<<(void const*) ...
      ==19427==    by 0x8048A17: main (errores_comunes.cpp:142)
      ==19427==
      ==19427== Conditional jump or move depends on uninitialised value(s)
      ==19427==    at 0x41431A81: std::ostreambuf_iterator > ...
      ==19427==    by 0x41431B4F: std::num_put ...
      ==19427==    by 0x414422C3: std::ostream::operator<<(void const*) ...
      ==19427==    by 0x8048A17: main (errores_comunes.cpp:142)
      ==19427==
      ==19427== Use of uninitialised value of size 4
      ==19427==    at 0x8048A2F: main (errores_comunes.cpp:143)

En general, puedes ignorar la mayor parte de la información y concentrarte en las referencias a tu programa. En este caso, interesan las líneas del mensaje de error que mencionan el fichero errores_comunes.cpp. Resulta claro que debe haber algún error entre las líneas 142 y 143, en la función main.

Una vez terminada la ejecución de tu programa, Valgrind mostrará un resumen de los errores detectados (ERROR SUMMARY) y otro de la memoria no liberada (LEAK SUMMARY). El aspecto es el siguiente:


      ==4438== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 16 from 1)
      ==4438== malloc/free: in use at exit: 16 bytes in 4 blocks.
      ==4438== malloc/free: 4 allocs, 0 frees, 16 bytes allocated.
      ==4438== For counts of detected errors, rerun with: -v
      ==4438== searching for pointers to 4 not-freed blocks.
      ==4438== checked 106496 bytes.
      ==4438==
      ==4438== 16 bytes in 4 blocks are definitely lost in loss record 1 of 1
      ==4438==    at 0x1B90939A: operator new(unsigned) (vg_replace_malloc.c:132)
      ==4438==    by 0x8048C9E: main (errores_comunes.cpp:226)
      ==4438==
      ==4438== LEAK SUMMARY:
      ==4438==    definitely lost: 16 bytes in 4 blocks.
      ==4438==      possibly lost: 0 bytes in 0 blocks.
      ==4438==    still reachable: 0 bytes in 0 blocks.
      ==4438==         suppressed: 0 bytes in 0 blocks.
      ==4438== Reachable blocks (those to which a pointer was found) are not shown.
      ==4438== To see them, rerun with: --show-reachable=yes

Para interpretar bien esta información debes tener en cuenta que malloc es lo mismo que new, free es lo mismo que delete y block es lo mismo que variable dinámica.

Observa que el resumen de errores dice que se han ejecutado 4 malloc (new) y 0 free (delete). Por otro lado, el resumen de memoria no liberada dice que hay 4 blocks (variables) perdidas. Esto quiere decir que el programa ha creado 4 variables dinámicas que no llegó a destruir.

¿Cómo uso los programas compila y ejecuta?

Utilizar Valgrind requiere pasar muchos parámetros a g++ y Valgrind. Los programas compila y ejecuta simplifican el uso de Valgrind, pasando los parámetros necesarios por defecto. Si no vas a usar Valgrind, no es necesario que utilices compila y ejecuta.

Si vas a usar Valgrind, descarga compila y ejecuta en tu directorio de trabajo. Una vez descargados, ejecuta las siguientes órdenes:


       $ chmod +x compila
       $ chmod +x ejecuta

Para compilar un programa C++, en lugar de usar la orden g++ con todos sus parámetros, puedes usar compila. Por ejemplo:


       $ ./compila ejemplo.cpp

en la pantalla debe aparecer un mensaje como el siguiente:


      g++ -Wall -Wextra -ansi -pedantic -g -O0 ejemplo.cpp

es decir, compila equivale a llamar a g++ con todos los parámetros necesarios para usar Valgrind. Si la compilación se ha completado con éxito, habrás obtenido un ejecutable a.out

Para ejecutar a.out, en lugar de usar Valgrind directamente, puedes teclear:


       $ ./ejecuta a.out

que equivale a llamar a Valgrind con los parámetros adecuados.


Pablo López  (lopez@lcc.uma.es)