Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: m@o_614 en 15 Marzo 2014, 19:31 pm



Título: dudas con strcpy
Publicado por: m@o_614 en 15 Marzo 2014, 19:31 pm
Saludos

tengo una duda con respecto al uso de strcpy

si tengo una cadena por ejemplo:

char *cadena = "hola mundo";

y despues quiero cambiar el contenido de esa cadena, puedo usar el strcpy para eso??

strcpy(cadena,"lo que quieras escribir");

gracias


Título: Re: dudas con strcpy
Publicado por: Yoel Alejandro en 15 Marzo 2014, 20:32 pm
Ok, hay varios aspectos implícitos en tu pregunta. La función

strcpy( char *s1, const char *s2)

copia el contenido de s2 en s1, añadiendo al final de s1 un carácter nulo de terminación '\0'. La única consideración es que s1 contenga el suficiente espacio disponible para copiar todos los caracteres (al menos la longitud de s2 más uno).

Esto por supuesto requiere que se pueda escribir en el espacio asignado a s1. Si declaras

char *cadena = "hola mundo";

entonces cadena se considerará un string constante y ya no podrás escribir nada nuevo en él. Pero la declaración:

char cadena[] = "hola mundo";

sí permite rescribir el contenido de cadena (siempre y cuando la nueva cadena sea de longitud menor o igual a la que tenías primero, de lo contrario no habrá espacio disponible suficiente). Por ejemplo, puedes poner:

strcpy( cadena, "Pepe");
puts( cadena );

y verás como ahora escribe "Pepe".

----------------------------------------------------------------------------------
NOTA. En cuánto a la declaración de string constantes no se con exactitud si la forma de interpretar la sentencia depende del compilador o de la versión de C, o de si es en C ó en C++. Que alguno de los usuarios más versados en el tema por favor nos complemente.


Título: Re: dudas con strcpy
Publicado por: milx86 en 15 Marzo 2014, 20:48 pm
Concuerdo con yoel, tienes que tener en cuenta 2 puntos importantes:

1. Cito: "Es nuestra responsabilidad que en la cadena de destino haya suficiente espacio reservado para copiar lo que queremos. Si no es así, estaremos sobreescribiendo direcciones de memoria en las que no sabemos qué hay."
Fuente http://www.nachocabanes.com/c/curso/cc05.php (http://www.nachocabanes.com/c/curso/cc05.php)

Asi que mejor te recomiendo usar strncpy.

2. Estas declarando una constante de caracteres que no puedes modificar.
Para no hacer copia y pega, te invito a leer esto, no es mucho y estoy seguro aclarará un poco más tus dudas.
Fuente: http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C/Punteros#Punteros_a_cadenas_de_caracteres (http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C/Punteros#Punteros_a_cadenas_de_caracteres)

Suerte. :D


Título: Re: dudas con strcpy
Publicado por: leosansan en 15 Marzo 2014, 21:21 pm
Concuerdo con yoel, tienes que tener en cuenta 2 puntos importantes:
......................................

Totalmente de acuerdo con ambos. Cito una indicación de la página de
cplusplus (http://www.cplusplus.com/reference/cstring/strcpy/):

Citar

strcpy

char * strcpy ( char * destination, const char * source );

Copy string
Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.



Sin embargo ..... supongo que es el propio compilador el que lleva a cabo la operación de ampliar el espacio de la cadena receptora, que en principio es de 10 y podría provocar el overflow, pero en la práctica eso no sucede.

Está es la salida al código de ejemplo:


Citar

Cadena str1 antes del cambio:
A ver si cabe la cadena str1 en srt2

Cadena str2 antes del cambio:
leosansan

Lo que queda en str1: abe la cadena str1 en srt2

Copiado, nuevo str2:
A ver si cabe la cadena str1 en srt2



Y la cadena str2 impresa caracter a caracter:
 A ver si cabe la cadena str1 en srt2

longitud de la nueva cadena str2=
36
Process returned 0 (0x0)   execution time : 1.500 s
Press any key to continue.


Como pueden observar, str2 "se metió" en la cadena str1 que originalmente erra de dimensión 10 sin problemas.

Eso si, destacar que si str2 no tiene suficiente dimensión quedará en la  cadena original el "resto" de la dicha cadena:


Código
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main ()
  4. {
  5.    int i;
  6.    char str1[ ]="A ver si cabe la cadena str1 en srt2";
  7.    char str2[10]="leosansan";
  8.    printf ("\nCadena str1 antes del cambio:\n%s",str1);
  9.    printf ("\n\nCadena str2 antes del cambio:\n%s",str2);
  10.    strcpy (str2,str1);
  11.    printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  12.    printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  13.    for( i=0;str2[i]; i++)
  14.      printf( "%c",str2[i] );
  15.    printf( "\n\nlongitud de la nueva cadena str2=\n%d", strlen(str2) );
  16.    return 0;
  17. }

Espero no haberme liado con este rollito, ¿o tal vez si?. Ya me contaran ustedes.

¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)



Título: Re: dudas con strcpy
Publicado por: amchacon en 15 Marzo 2014, 21:33 pm
NOTA. En cuánto a la declaración de string constantes no se con exactitud si la forma de interpretar la sentencia depende del compilador o de la versión de C, o de si es en C ó en C++. Que alguno de los usuarios más versados en el tema por favor nos complemente.
Te refieres a:
Código
  1. char *cadena = "hola mundo";

En ningún compilador de C/C++ podrás modificar el contenido de cadena. Más que nada porque lo que estás haciendo es declarar un puntero que apunta a una cadena literal (y los valores literales son constantes por definición).

Excepción los arrays de chars, donde por comodidad sintáctica se te permite escribir esta sentencia:
Código
  1. char cadena[] = {'h','o','l','a',' ','m','u','n','d','o','\0'};

De esta forma:
Código
  1. char cadena[] = "hola mundo";

Totalmente de acuerdo con ambos. Cito una indicación de la página de
cplusplus (http://www.cplusplus.com/reference/cstring/strcpy/):

Sin embargo ..... supongo que es el propio compilador el que lleva a cabo la operación de ampliar el espacio de la cadena receptora, que en principio es de 10 y podría provocar el overflow, pero en la práctica eso no sucede.

Está es la salida al código de ejemplo:


Como pueden observar, str2 "se metió" en la cadena str1 que originalmente erra de dimensión 10 sin problemas.

Eso si, destacar que si str2 no tiene suficiente dimensión quedará en la  cadena original el "resto" de la dicha cadena:


Código
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main ()
  4. {
  5.    int i;
  6.    char str1[ ]="A ver si cabe la cadena str1 en srt2";
  7.    char str2[10]="leosansan";
  8.    printf ("\nCadena str1 antes del cambio:\n%s",str1);
  9.    printf ("\n\nCadena str2 antes del cambio:\n%s",str2);
  10.    strcpy (str2,str1);
  11.    printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  12.    printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  13.    for( i=0;str2[i]; i++)
  14.      printf( "%c",str2[i] );
  15.    printf( "\n\nlongitud de la nueva cadena str2=\n%d", strlen(str2) );
  16.    return 0;
  17. }

Espero no haberme liado con este rollito, ¿o tal vez si?. Ya me contaran ustedes.
Pues si te has liado, prueba este otro código:

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main ()
  5. {
  6.    int i;
  7.    char str1[ ]="Cosita grande y hermosa";
  8.    char str2[10]="leosansan";
  9.  
  10.    printf ("\nCadena str1 antes del cambio:\n%s",str1);
  11.    printf ("\n\nCadena str2 antes del cambio:\n%s",str2);
  12.    strcpy (str2,str1);
  13.    printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  14.    printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  15.    for( i=0; str2[i]; i++)
  16.        printf( "%c",str2[i] );
  17.    printf( "\n\nlongitud de la nueva cadena str2=\n%d\n", strlen(str2) );
  18.  
  19.    return 0;
  20. }
  21.  

Fijate en el contenido de str1:
Citar
nde y hermosa

Ha dado overflow, te has salido del espacio de str2 y estás sobreescribiendo la cadena str1.

¿Por que en el otro ejemplo si funcionaba? Pues porque lo has interpretado mal:
Citar
A ver si cabe la cadena str1 en srt2
abe la cadena str1 en srt2

Se ha comido lo que está en negrita, lo que pasa esque no suena raro y lo has pasado por alto sin darte cuenta.


Título: Re: dudas con strcpy
Publicado por: Yoel Alejandro en 15 Marzo 2014, 21:44 pm
En el caso que plantea leosansan, supongo que dependerá del compilador. Probablemente cualquier compilador moderno tomará previsiones adecuadas si la cadena de origen excede en longitud a la cadena de destino.

Sin embargo la filosofía de C es que "los programadores saben lo que están haciendo", por eso pienso que a pesar de la ayuda que pueda proporcionar el compilador, un buen programador de C debe garantizarse por sí mismo la consistencia de este tipo de operaciones. Así que al autor del post: pues, ya lo sabes!!!

Interesante un detalle adicional de la cita de cplusplus y que se me había pasado por alto: Las cadenas origen y destino no deben solaparse. Esta es otra consideración a tener en cuenta, no se debe intentar escribir una cadena sobre sí misma.

NOTA: Estas dificultades se evitan cómodamente usando la clase string de C++. Pero personalmente creo que quién se quiera formar como un verdadero programador debería pasar primero por C (con todas sus dificultades asociadas), y luego ir a C++. Hay que "ensuciarse" primero para aprender bien. Bueno, al menos es mi criterio, no se qué opinen los demás (?)


Título: Re: dudas con strcpy
Publicado por: amchacon en 15 Marzo 2014, 22:40 pm
En el caso que plantea leosansan, supongo que dependerá del compilador. Probablemente cualquier compilador moderno tomará previsiones adecuadas si la cadena de origen excede en longitud a la cadena de destino.
Eso no es así, ya se lo desmentí a Leosan.

Hacer esas aproximaciones suele dar muy malos resultados. Luego ocurren los errores extraños y inexplicables, los crasheos aleatorios...

Como regla general, siempre hay que ceñirse a lo que te diga el creador de la función. Cualquier truco o atajo que intentes no está garantizado contra errores y probablemente acabe muy mal.

Estas dificultades se evitan cómodamente usando la clase string de C++. Pero personalmente creo que quién se quiera formar como un verdadero programador debería pasar primero por C (con todas sus dificultades asociadas), y luego ir a C++. Hay que "ensuciarse" primero para aprender bien. Bueno, al menos es mi criterio, no se qué opinen los demás (?)
Un buen programador es el que es capaz de escribir la solución de un problema paso a paso. El usar un lenguaje u otro solo sirve para probar tu solución en una computadora.

Lo que si es verdad esque es importante conocer los mecanismos de abstracción (grafos, listas, vectores...). Y una forma práctica de aprenderlo es re-hacer la librería básica del lenguaje.

PD: Un buen programador no tiene problemas en adaptarse de un lenguaje a otro en poco tiempo. Aunque no lo haya visto nunca, normalmente son todos muy parecidos.


Título: Re: dudas con strcpy
Publicado por: leosansan en 15 Marzo 2014, 23:24 pm
................................................

Fijate en el contenido de str1:
Ha dado overflow, te has salido del espacio de str2 y estás sobreescribiendo la cadena str1.

¿Por que en el otro ejemplo si funcionaba? Pues porque lo has interpretado mal:
Se ha comido lo que está en negrita, lo que pasa es que no suena raro y lo has pasado por alto sin darte cuenta.

La salida tuya:

Citar

Cadena str1 antes del cambio:
Cosita grande y hermosa

Cadena str2 antes del cambio:
leosansan

Lo que queda en str1: nde y hermosa

Copiado, nuevo str2:
Cosita grande y hermosa



Y la cadena str2 impresa caracter a caracter:
 Cosita grande y hermosa

longitud de la nueva cadena str2=
23

Process returned 0 (0x0)   execution time : 0.102



Por ejemplo esta otra salida:

Citar

Cadena str1 antes del cambio:
Cosita grande y hermosa

Cadena str2 antes del cambio:
leosansan

Lo que queda en str1: nde y hermosa

Copiado, nuevo str2:
Cosita grande y hermosa



Y la cadena str2 impresa caracter a caracter:
 Cosita grande y hermosa

Cadena str0 despues del cambio:

Gran Canaria es Cosita grande y hermosa

Cadena str3 despues del cambio:

leosansan es de Gran Canaria


Process returned 0 (0x0)   execution time : 1.165 s
Press any key to continue.



A este código:

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main ()
  5. {
  6.    int i;
  7.    char str0[ ]="Gran Canaria es Cosita grande y hermosa";
  8.    char str1[  ]="Cosita grande y hermosa";
  9.    char str2[ ]="leosansan";
  10.    char str3[ ]="leosansan es de Gran Canaria";
  11.  
  12.    printf ("\nCadena str1 antes del cambio:\n%s",str1);
  13.    printf ("\n\nCadena str2 antes del cambio:\n%s",str2);
  14.    strcpy (str2,str1);
  15.    printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  16.    printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  17.    for( i=0; str2[i]; i++)
  18.        printf( "%c",str2[i] );
  19.    printf( "\n\nCadena str0 despues del cambio:\n\n%s",str0);
  20.    printf ("\n\nCadena str3 despues del cambio:\n\n%s\n\n",str3);
  21.    return 0;
  22. }

Lo realmente raro es que si declaro varias cadenas str, tres o cuatro, además de str1 y str2, lo que queda en str1 siempre es el resto de lo que teóricamente "sobra en str2, en tu ejemplo y en el mio. Será casualidad o no, pero es un resultado cuando menos inesperado. ¿Por qué juntamente queda en str1 y no en las otras cadenas que he declarado?. He ahí mi gran duda, ¿será casualidad o es un comportamiento normal de la función strcpy "dejar" el resto de la cadena que no quepa en str2?. Y a los ejemplos me remito, claro que tan sólo son dos ejemplos y ya sabemos que no se puede pasr del particular al general, pero ......

Pero lo que dice el manual está claro, yo sólo pongo de manifiesto un hecho contrastado en esos dos ejemplos.


¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)


Título: Re: dudas con strcpy
Publicado por: Yoel Alejandro en 15 Marzo 2014, 23:26 pm
Amchacon, por eso dije ... "supongo", porque la verdad yo no se.

No lo se porque en lo particular siempre trato de programar de forma "segura", atendiendo a las especificaciones de la biblioteca estándar de C, y la verdad pocas veces me he puesto a ver qué pasa si hago tal o cuál cosa de manera insegura. Prefiero irme de una vez por lo sano, y así evitar crasheos, etc.

Pero respecto a lo de varios lenguajes lo que quise decir es que específicamente en el aprendizaje de C/C++, me parece más adecuado aprender primero a programar correctamente en C, y luego pasarse a C++ el cual básicamente amplica las capacidades del primero. Eso en lugar de mezclar el C y el C++, lo que constituye una mala costumbre según el criterio unánime creo que todos los que opinamos aquí.

Otra mala costumbre, y lamentablemente extendida entre algunos maestros, es no guiarse por el C estándar. Referirse a gotoxy(), getch(), etc., de la manera más natural (y descarada), sin especificar ni aclarar que son no funciones estándares. No digo que no usen esas funciones, pero deben dejar en claro que son privativas y generalmente usadas en Windows.


Título: Re: dudas con strcpy
Publicado por: amchacon en 15 Marzo 2014, 23:50 pm
He ahí mi gran duda, ¿será casualidad o es un comportamiento normal de la función strcpy "dejar" el resto de la cadena que no quepa en str2?. Y a los ejemplos me remito, claro que tan sólo son dos ejemplos y ya sabemos que no se puede pasr del particular al general, pero ....
Tengo una teoría.

Suponte este caso:

Código
  1. char inicia[] = "Entrada larga";
  2. char destino[5] = "abcd";

La memoria podría ser:
Citar
00 a   --> Destino
01 b
02 c
03 d
04 '\0'
05 E   --> Original
06 n
07 t
08 r
09 a
10 ' '
11 l
12 a
13 r
14 g
15 a
16 '\0'

Y hacemos el strcpy:

Citar
00 E    --> Destino
01 n
02 t
03 r
04 a
05 d    --> Original
06 a
07 ' '
08 l
09 a
10 r
11 g
12 a
13 '\0'
14 g
15 a
16 '\0'

Fijate que el puntero "original" apunta a las ultimas posiciones de "destino".

No esque deje lo que sobre, esque estamos viendo los ultimos caracteres de la cadena destino!

Lo acabo de probar y efectivamente str1 da la salida que esperaba: "da larga"
Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main ()
  5. {
  6.    char str1[] = "Entrada larga";
  7.    char str2[5] = "abcd";
  8.  
  9.    strcpy (str2,str1);
  10.    printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  11.    printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  12.    int i;
  13.    for( i=0; str2[i]; i++)
  14.        printf( "%c",str2[i] );
  15.    return 0;
  16. }
  17.  

Podríamos "rematar" la teoría modificando un valor de str2. A ver si esos cambios aparecen en str1:

Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main ()
  5. {
  6.    char str1[] = "Entrada larga";
  7.    char str2[5] = "abcd";
  8.  
  9.    strcpy (str2,str1);
  10.  
  11.  
  12.    str2[10] = 'Z';
  13.  
  14.    printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  15.    printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  16.    int i;
  17.    for( i=0; str2[i]; i++)
  18.        printf( "%c",str2[i] );
  19.    return 0;
  20. }
  21.  

Cuya salida es:

(https://dl.dropboxusercontent.com/u/69551225/Captura%20de%20pantalla%202014-03-15%2023.46.15.png)

¡La Z aparece en las dos cadenas! Parece que mi teoria tiene sentido : )

No lo se porque en lo particular siempre trato de programar de forma "segura", atendiendo a las especificaciones de la biblioteca estándar de C, y la verdad pocas veces me he puesto a ver qué pasa si hago tal o cuál cosa de manera insegura. Prefiero irme de una vez por lo sano, y así evitar crasheos, etc.
Eso está bien ^^

Pero respecto a lo de varios lenguajes lo que quise decir es que específicamente en el aprendizaje de C/C++, me parece más adecuado aprender primero a programar correctamente en C, y luego pasarse a C++ el cual básicamente amplica las capacidades del primero. Eso en lugar de mezclar el C y el C++, lo que constituye una mala costumbre según el criterio unánime creo que todos los que opinamos aquí.
Bueno yo hize eso, aunque mucha gente empezó a programar en Python o en Java.

Luego no tuvieron problema en pasar de un lenguaje a otro. Como siempre, lo importante no es el lenguaje sino en saber describir la solución a un problema.

Otra mala costumbre, y lamentablemente extendida entre algunos maestros, es no guiarse por el C estándar. Referirse a gotoxy(), getch(), etc., de la manera más natural (y descarada), sin especificar ni aclarar que son no funciones estándares. No digo que no usen esas funciones, pero deben dejar en claro que son privativas y generalmente usadas en Windows.
+1, totalmente de acuerdo.


Título: Re: dudas con strcpy
Publicado por: leosansan en 16 Marzo 2014, 00:11 am

La salida que obtienes viene a corroborar el origen de mi duda:

Citar

Lo que queda en str1: da laZga

Copiado, nuevo str2:
Entrada laZga



Y la cadena str2 impresa caracter a caracter:
 Entrada laZga
Process returned 0 (0x0)   execution time : 2.366 s
Press any key to continue.


Lo que queda en str1 es justo lo que cabe "oficialmente" en str2 ..... otra vez, y van tres ejemplos.
 Fíjate que "da laZga" es justo la cadena str1 sin los 5 caracteres que "cabrían" en str2" de acuerdo a su dimensión. Curioso, ¿no?.

Aclaro mis sospechas:

* En str2 se copia la cadena str1, aunque str2 tenga declarada una dimensión inferior a str1. Eso sucede en los tres ejemplos aquí expuestos, hasta ahora.

* En str1 "queda" lo que "teóricamente" no cabia en str1 por tener menor dimensión.

Como ves tu ejemplo ha venido a ratificar mis sospechas en lugar de negarlo.

Y ¡ojo!, no digo que esto sea una regla general, que de acuerdo a las indicaciones del comportamiento de strcpy vulnera, pero...... los ejemplos parecen empeñarse en dar la razón a mis dudas ... por ahora.

Y repito, sólo es una sospecha de lo que puede ser un comportamiento general y no previsto.


¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)


Título: Re: dudas con strcpy
Publicado por: amchacon en 16 Marzo 2014, 00:28 am
La salida que obtienes viene a corroborar el origen de mi duda:

Lo que queda en str1 es justo lo que cabe "oficialmente" en str2 .....
Vuelve a repasar mi mapa de memoria, ahi explico porque str2 puede almacenar una cadena mayor (comiendole memoria a otras variables ;D).

* En str1 "queda" lo que "teóricamente" no cabia en str1 por tener menor dimensión.
Podemos usar memoria dinamica, de esa forma nos aseguramos que no ocupan posiciones continuas y no se cumpla mi teoría:
Código
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main ()
  5. {
  6.    int tam = strlen("Entrada larga");
  7.   char* str1 = malloc(tam+1);
  8.   strcpy (str1,"Entrada larga");
  9.  
  10.   char str2[5] = "abcd";
  11.  
  12.   strcpy (str2,str1);
  13.   printf ("\n\nLo que queda en str1: %s\n\nCopiado, nuevo str2:\n%s\n\n",str1,str2);
  14.   printf("\n\nY la cadena str2 impresa caracter a caracter:\n ");
  15.   int i;
  16.   for( i=0; str2[i]; i++)
  17.       printf( "%c",str2[i] );
  18.   return 0;
  19. }
  20.  

Tu regla no se cumple ya que da un error de ejecución en el segundo strcpy. Probablemente porque estemos escribiendo fuera de nuestra memoria reservada.

Fíjate que "da laZga" es justo la cadena str1 sin los 5 caracteres que "cabrían" en str2" de acuerdo a su dimensión. Curioso, ¿no?.
La Z fue introducida después del strcpy, eso quiere decir que esa letra era "común" en las dos cadenas (vuelvo a hacer referencia a mi esquema de la memoria).


Título: Re: dudas con strcpy
Publicado por: leosansan en 16 Marzo 2014, 00:38 am
Tutti claro ahora, se debía a lo que comentas de posiciones memorias seguidas.

¡¡¡¡ Saluditos! ..... !!!!


(http://st.forocoches.com/foro/images/smilies/aaaaa.gif)