[C con Clase] Cadenas estilo C

Sergio Torró storro.zgz en gmail.com
Mie Ago 7 16:59:50 CEST 2013


Muchas gracias por tu respuesta Salvador, te has explicado perfectamente :-)

Deduzco que si quisiera concatenar cadenas que el usuario introduce por
teclado, tendría que asignar memoria dinámicamente para poder hacerlo,
¿verdad? He escrito el siguiente código y me funciona bien, pero si hay
algún detalle de los que no se ven a simple vista me encantaría saberlo ;-P

int main(void)

{

       char *cad_input = new char;

       char cad_concat[] = "-> concatenado con este texto.";

       char *cad_final = new char;



       cout << "Introduce una cadena: ";

       cin >> cad_input;



       strcat(cad_final, cad_input);

       strcat(cad_final, " ");

       strcat(cad_final, cad_concat);



       cout << endl << cad_final << endl;



       delete cad_input;

       delete cad_final;



       return 0;

}

Y en este último caso imagino que es mejor utilizar la asignación dinámica
de memoria de C++ y evitar malloc() y realloc(), ¿verdad?

Un saludo y gracias por tu tiempo.


El 6 de agosto de 2013 20:47, Salvador Pozo <salvador en conclase.net>escribió:

> El pasado 2013-08-06 16:48:20, Sergio Torró escribió:
>
> ST> Muy buenas a todos. Me gustaría comentar una duda (seguramente muy
> tonta)
> ST> sobre el manejo de cadenas estilo C. Tengo el siguiente código que
> funciona
> ST> perfectamente:
> ST> #include <iostream>
> ST> using std::cout;
> ST> using std::endl;
> ST> #include <cstring>
> ST> using std::strcat;
> ST> int main(void) {
> ST>        char cadena1[] = "Hola";
> ST>        char cadena2[] = "Perico";
> ST>        strcat(cadena1, " ");
> ST>        strcat(cadena1, cadena2);
> ST>        cout << cadena1 << endl;
> ST>        return 0;
> ST> }
> ST> Echándole un ojo a la declaración de strcat veo que sus parámetros son
> ST> punteros (char*, const char*) y se me ocurrió probar a declarar mis dos
> ST> cadenas de caracteres como tales: char *cadena1 = "Hola"; Si hago ese
> ST> cambio, no funciona.
> ST> Creo entender la diferencia entre un array de chars y un puntero: si
> no me
> ST> equivoco el array reserva espacio para cada char que contiene la
> cadena. Si
> ST> lo declaro como un puntero, se reserva espacio para almacenar
> simplemente
> ST> un puntero que puede apuntar a cualquier cadena de chars. Mi duda es:
> si
> ST> esa función está esperando un puntero a una cadena, ¿no debería
> funcionar
> ST> si le paso eso mismo? ¿No se supone que el nombre del array (en este
> caso
> ST> cadena1) es un puntero al primer elemento de la cadena?
>
> A ver si puedo explicar con claridad lo que está pasando en este ejemplo.
>
> Cuando declaras las cadenas como arrays, el compilador, como bien dices,
> reserva espacio suficiente para contener la cadena declarada.
>
> El problema no es tanto cuanta memoria reserva el compilador como dónde la
> reserva.
>
> Cuando se trata de un array, la memoria para las cadenas se obtiene de la
> pila, y los literales de inicialización, en este caso "Hola" y "Perico" se
> copian desde su origen inicial a las direcciones asignadas a los arrays.
>
> En este caso, el programa funciona porque se dan algunas circunstancias
> "afortunadas".
>
> Una de ellas es que las cadenas son cortas, pero al copiar una cadena a
> continuación de la otra, estamos corrompiendo la memoria que no pertenece
> al array, y por lo tanto, corrompemos la pila. Como son cadenas cortas,
> tenemos al suerte de no corromper dados sensibles, pero si fueran cadenas
> más largas esto no sería así.
>
> La otra es el orden en que hemos declarado las variables.
>
> Prueba esto:
> ----8<------
> #include <iostream>
> using std::cout;
> using std::endl;
>
> #include <cstring>
> using std::strcat;
>
> int main(void) {
>        char cadena1[] = "Hola";
>        char cadena2[] = "Perico";
>        cadena2[6] = ' ';
>        cout << cadena2 << endl;
>        return 0;
> }
> ----8<------
>
> La salida, en este caso, es "Perico Hola". Esto es porque al almacenarse
> en la pila, las variables se crean de arriba a abajo, es decir, cada
> variable local tiene direcciones de memoria menores que las anteriores. El
> resultado es que primero se almacena en la pila "Hola", y a continuación,
> en posiciones correlativas, pero menores, "Perico", con el carácter nulo
> terminador.
>
> En este programa hemos sustituido el terminador de "Perico" por un
> espacio, y por eso se concatenan (aparentemente) las dos cadenas.
>
> Si declaras las variables en otro orden, probablemente destruyas una al
> intentar concatenarlas.
>
> Cuando sustituyes las cadenas por punteros, los punteros se siguen
> almacenando en la pila, pero en lugar de copiar las cadenas al inicial el
> programa, sólo copia las direcciones. Las cadenas se almacenan junto con el
> programa ejecutable.
>
> La mayor diferencia en este caso es que el sistema operativo protege las
> direcciones de memoria donde se almacena el programa para que no puedan ser
> modificadas, o al menos para detectar cuando han sido modificadas. Esto es
> lo que provoca la excepción "segmentation fault".
>
> Una pista en este caso son los mensajes de aviso del compilador:
>
> \main.cpp|9|warning: deprecated conversion from string constant to 'char*'
> [-Wwrite-strings]|
>
> Ya te está avisando que estás intentando convertir una cadena constante en
> un puntero a char, y que eso está desaconsejado.
>
> Constante significa que no puede modificarse su valor, y eso es
> precisamente lo que intentas hacer con strcat.
>
> En cualquiera de los dos casos estamos diseñando mal el programa, ya que
> intentamos copiar cadenas usando objetos (punteros o arrays), que no tienen
> la capacidad suficiente para contenerlas.
>
> ST> He pensado que
> ST> tiene que ver con la implementación de la función strcat la cual me
> ST> gustaría ver, ¿hay alguna forma donde pueda ver la implementación de
> dichas
> ST> funciones? Vengo de Java y estoy muy acostumbrado en Eclipse a hacer un
> ST> “Control + clic” en el elemento que quiero ver su implementación ^^
> ST> Imagino que para el compilador de GNU no será un problema ver la
> ST> implementación (¿simplemente descargar las fuentes verdad?). ¿Es
> posible
> ST> hacerlo también en Visual Studio? Uso Code::Blocks y Visual Studio a
> partes
> ST> iguales.
>
>
> No he encontrado los fuentes para strcat, tampoco he buscado mucho, la
> verdad. :) Sobre todo porque la implementación de esa función no tiene
> importancia en este caso.
>
> Una posible implementación podría ser:
>
> ----8<------
> char *strcat(char *dst, const char *src)
> {
>     char *ret = dst;
>
>     for (; *dst; ++dst);
>     while ((*dst++ = *src++) != '\0');
>     return ret;
> }
> ----8<------
>
> Sin embargo, estas funciones ni siquiera tienen por qué estar escritas en
> C, ya que generalmente se trata de librerías de uso frecuente, suelen estar
> muy optimizadas, y frecuentemente se escriben en ensamblador.
>
> Hasta pronto.
>
> --
> Salvador Pozo (Administrador)
> mailto:salvador en conclase.net
> Blog con Clase: http://blogconclase.wordpress.com
> Con Clase: http://conclase.net
> _______________________________________________
> Lista de correo Cconclase Cconclase en listas.conclase.net
> http://listas.conclase.net/mailman/listinfo/cconclase_listas.conclase.net
> Bajas: http://listas.conclase.net/index.php?gid=2&mnu=FAQ
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.conclase.net/pipermail/cconclase_listas.conclase.net/attachments/20130807/7f75a55e/attachment.html>


Más información sobre la lista de distribución Cconclase