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; }
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á:
Invalid write
, Invalid read
,...)
errores_comunes.cpp
, línea 168, función main()
)
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.
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.