c++ c++11 memory-leaks stringstream temporary-objects

Error de memoria con std: ostringstream y-std=c++ 11?



c++11 memory-leaks (2)

Eche un vistazo más de cerca a esta línea: const char* stmt = qs.str().c_str();

qs.str() proporciona un objeto std::string temporal desde el cual toma un puntero a la matriz char* interna. Una vez que esta línea completa su ejecución, su puntero ya no es válido y puede haber algo más (y probablemente) almacenado allí.

Intenta hacer esto en su lugar:

std::string strstmt(qs.str()); const char* stmt = strstmt.c_str();

Esta pregunta ya tiene una respuesta aquí:

EDITAR : Gracias a todos los que señalaron el problema, y ​​que se discutió en Stack Overflow. Eché el último voto cerca yo mismo.

Una pregunta relacionada: ni CPP Reference en ostringstream o ostringstream::str state es temporal. ¿Cómo sabía tanta gente? ¿O hay documentación diferente que debería haber consultado?

Estoy teniendo muchos problemas con los errores de memoria en Debian 7.3 (x64) con GCC 4.7.2, -std=c++11 y std::ostringstream . Esto conduce a resultados bizaare como https://stackoverflow.com/questions/21260815/which-r-in-this-create-table-error-message .

El programa completo usa Sqlite. Ejecutar un caso reducido desde la línea de comandos frente a Valgrind imprime 2 errores diferentes. Todo el programa de casos reducidos está disponible en Code Viewer (pensé que era un poco largo publicar toda la muestra aquí). Todo lo que hace es inicializar Sqlite, abre una base de datos, crea una tabla, cierra la base de datos y unifica la base de datos. E informa errores si ocurren. No hay nada más que esté ocurriendo.

Aquí hay parte del programa de casos reducidos que simplemente intenta crear una tabla. Si la tabla existe, debe producir un error (que sí lo hace):

ostringstream qs; qs.str().reserve(96); qs << "CREATE TABLE test"; qs << "("; qs << " userid INTEGER PRIMARY KEY AUTOINCREMENT,"; qs << " username TEXT,"; qs << " salt BLOB,"; qs << " hmac BLOB"; qs << ");"; const char* stmt = qs.str().c_str(); AC_ASSERT(NULL != stmt); rc = sqlite3_exec(db, stmt, NULL, NULL, &err); AC_ASSERT(rc == SQLITE_OK); if(rc != SQLITE_OK) { ostringstream oss; oss.str().reserve(96); oss << "sqlite3_exec failed, error " << rc; LogError(oss.str().c_str()); oss.clear(), oss.str(""); oss << "Sqlite error: " << err; LogError(oss.str().c_str()); // Break, handle error }

Sin embargo, debajo de la línea de comando, el mensaje es:

sqlite3_exec failed, error 1 Sqlite error: table

En Valgrind, el mensaje es:

sqlite3_exec failed, error 1 Sqlite error: table test already exists

El programa hace un uso intensivo de ostringstream , y Valgrind produce cerca de 13 problemas centrados en ellos, 9 de los cuales incluyen el operator delete(void*) en el basic_string subyacente. Por ejemplo, uno se muestra a continuación (y la línea 76 de t.cpp es const char* stmt = qs.str().c_str(); ):

==14318== Invalid read of size 1 ==14318== at 0x45ACC8: sqlite3_exec (sqlite3.c:94542) ==14318== by 0x405D07: main (t.cpp:79) ==14318== Address 0x5d89728 is 24 bytes inside a block of size 127 free''d ==14318== at 0x4C27870: operator delete(void*) (vg_replace_malloc.c:502) ==14318== by 0x530EB1F: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.17) ==14318== by 0x405CF1: main (t.cpp:76)

¿Alguien tiene alguna idea de lo que está pasando aquí? ¿Es ostringstream ? O tal vez GCC 4.7.2? ¿O tal vez el puerto de Debian?

(Y perdón por la pregunta abierta. Me he quedado sin cosas que hacer).


const char* stmt = qs.str().c_str();

Eso extrae una cadena temporal de qs , toma un puntero a su contenido y luego destruye el temporal dejando el puntero colgando. Usar el puntero después de esto dará un comportamiento indefinido.

Para solucionarlo, puede asignar el resultado de str() a una variable, de modo que ya no sea temporal, o usar esta expresión como argumento para sqlite3_exec , para que el temporal sobreviva hasta después de esa llamada de función. (En el segundo caso, tendría que eliminar la primera afirmación, pero esa afirmación es bastante inútil de todos modos).