usar resueltos leer funciones ejercicios ejemplos declarar como caracteres cadenas cadena arreglo c++

resueltos - funciones de cadenas de caracteres en c++



¿Por qué funciona esto: devolver el literal de la cadena C desde la función std:: string y llamar a c_str() (9)

Recientemente tuvimos una conferencia en la universidad donde nuestro profesor nos habló sobre diferentes cosas que debemos tener en cuenta al programar en diferentes idiomas. El siguiente es un ejemplo en C ++:

std::string myFunction() { return "it''s me!!"; } int main(int argc, const char * argv[]) { const char* tempString = myFunction().c_str(); char myNewString[100] = "Who is it?? - "; strcat(myNewString, tempString); printf("The string: %s", myNewString); return 0; }

La idea de por qué esto fallaría es que return "it''s me!!" llama implícitamente al constructor std :: string con un char []. Esta cadena se devuelve de la función y la función c_str() devuelve un puntero a los datos de la std::string .

Como la cadena devuelta por la función no está referenciada en ninguna parte, debe ser desasignada inmediatamente. Esa era la teoría.

Sin embargo, dejar que este código se ejecute funciona sin problemas. Sería curioso escuchar lo que piensas. ¡Gracias!


A menos que me esté perdiendo algo, creo que este es un tema de alcance. myFunction () devuelve un std :: string. El objeto de cadena no se asigna directamente a una variable. Pero permanece en alcance hasta el final de main (). Por lo tanto, tempString apuntará a un espacio perfectamente válido y disponible en la memoria hasta el final del bloque de código main (), momento en el cual el tempString también quedará fuera de alcance.


Como otros han mencionado de acuerdo con el estándar de C ++, este es un comportamiento indefinido.

La razón por la que esto "funciona" se debe a que la memoria se ha devuelto al gestor de almacenamiento dinámico que la conserva para su posterior reutilización. La memoria no se ha devuelto al sistema operativo y, por lo tanto, sigue perteneciendo al proceso. Es por eso que acceder a la memoria liberada no causa un error de segmentación. Sin embargo, el problema sigue siendo que ahora dos partes de su programa (su código y el administrador del montón o el nuevo propietario) están accediendo a la memoria que creen que les pertenece únicamente. Esto jode las cosas tarde o temprano.


Como se dijo anteriormente, es un comportamiento impredecible. No funciona para mí (en la configuración de depuración). El destructor std :: string se llama inmediatamente después de la asignación a tempString, cuando finaliza la expresión que utiliza el objeto de cadena temporal. Dejando que tempString apunte a una memoria liberada (que en su caso aún contiene los literales "¡Soy yo!").


Creo que la razón es que la memoria de la pila no se ha reescrito, por lo que puede obtener los datos originales. Creé una función de prueba y la llamé antes del strcat.

std::string myFunction() { return "it''s me!!"; } void test() { std::string str = "this is my class"; std::string hi = "hahahahahaha"; return; } int main(int argc, const char * argv[]) { const char* tempString = myFunction().c_str(); test(); char myNewString[100] = "Who is it?? - "; strcat(myNewString, tempString); printf("The string: %s/n", myNewString); return 0; }

Y consigue el resultado:

The string: Who is it?? - hahahahahaha

Esto probó mi idea.


El hecho de que la cadena esté desasignada no significa necesariamente que la memoria ya no sea accesible. Mientras no haga nada que pueda sobrescribirlo, la memoria seguirá siendo utilizable.


En realidad, los literales de cadena tienen una duración de almacenamiento estático. Están empaquetados dentro del ejecutable mismo. No están en la pila, ni asignados dinámicamente. En el caso habitual, es correcto que esto apunte a una memoria no válida y sea un comportamiento indefinido; sin embargo, para las cadenas, la memoria está en almacenamiento estático, por lo que siempre será válida.


No puedes concluir que no hay problemas obteniendo tu resultado por coincidencia.

Hay otros medios para detectar ''problemas'':

  • Análisis estático.
  • Valgrind detectaría el error, mostrándole tanto la acción ofensiva (que intenta copiar desde la zona liberada por Strcat) como la desasignación que causó la liberación.

Invalid read of size 1

at 0x40265BD: strcat (mc_replace_strmem.c:262) by 0x80A5BDB: main() (valgrind_sample_for_so.cpp:20) [...] Address 0x5be236d is 13 bytes inside a block of size 55 free''d at 0x4024B46: operator delete(void*) (vg_replace_malloc.c:480) by 0x563E6BC: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.13) by 0x80A5C18: main() (basic_string.h:236) [...]

  • La única manera verdadera sería probar que el programa es correcto. Pero es realmente difícil para el lenguaje de procedimiento, y C ++ lo hace más difícil.

Su análisis es correcto. Lo que tienes es un comportamiento indefinido . Esto significa que casi cualquier cosa puede pasar. Parece que en su caso la memoria utilizada para la cadena, aunque desasignada, aún conserva el contenido original cuando accede. Esto sucede a menudo porque el sistema operativo no borra la memoria desasignada. Simplemente lo marca como disponible para uso futuro. Esto no es algo con lo que deba lidiar el lenguaje C ++: es realmente un detalle de implementación del sistema operativo. En lo que se refiere a C ++, se aplica el "comportamiento indefinido" de alcance general.


Supongo que la desasignación no implica la limpieza de la memoria o la puesta a cero. Y, obviamente, esto podría llevar a una falla de seguridad en otras circunstancias.