¿Es posible modificar una cadena de caracteres en C?

81

He estado luchando durante unas horas con todo tipo de tutoriales de C y libros relacionados con los punteros, pero lo que realmente quiero saber es si es posible cambiar un puntero de caracteres una vez que se ha creado.

Esto es lo que he probado:

Entonces, ¿hay alguna forma de cambiar los valores dentro de las cadenas en lugar de las direcciones del puntero?

Matthew Stopa
fuente

Respuestas:

158

Cuando escribe una "cadena" en su código fuente, se escribe directamente en el ejecutable porque ese valor debe conocerse en el momento de la compilación (hay herramientas disponibles para separar el software y encontrar todas las cadenas de texto sin formato). Cuando escribe char *a = "This is a string", la ubicación de "Esto es una cadena" está en el ejecutable, y la ubicación a la que aapunta, está en el ejecutable. Los datos de la imagen ejecutable son de solo lectura.

Lo que debe hacer (como han señalado las otras respuestas) es crear esa memoria en una ubicación que no sea de solo lectura, en el montón o en el marco de la pila. Si declara una matriz local, se crea espacio en la pila para cada elemento de esa matriz, y el literal de cadena (que se almacena en el ejecutable) se copia en ese espacio en la pila.

también puede copiar esos datos manualmente asignando algo de memoria en el montón y luego usar strcpy()para copiar un literal de cadena en ese espacio.

Siempre que asigne espacio usando malloc()recuerde llamar free()cuando haya terminado (lea: pérdida de memoria).

Básicamente, debes realizar un seguimiento de dónde están tus datos. Cada vez que escribe una cadena en su fuente, esa cadena es de solo lectura (de lo contrario, estaría cambiando potencialmente el comportamiento del ejecutable; imagínese si escribió char *a = "hello";y luego cambió a[0]a 'c'. Luego, en otro lugar escribió printf("hello");. Si se le permitiera cambiar el primero carácter de "hello", y su compilador solo lo almacenó una vez (debería), ¡luego printf("hello");saldría cello!

Carson Myers
fuente
12
La última sección me explicó mucho sobre por qué es necesario ser de solo lectura. Gracias.
CDR
1
-1: no dice que use const char *, y nada garantiza que las cadenas literales se almacenen en la memoria ejecutable.
Bastien Léonard
No, no necesita const para las dos soluciones que he dado, además, si la cadena se conoce en tiempo de compilación y se compila en el ejecutable, ¿dónde más se almacenaría? En gcc, si escribo char * a = "hallo."; o char b [] = "hola.";, entonces el ensamblaje genera "LC0: .ascii" Hallo. \ 0 "LC1: .ascii" Hola. \ 0 "" ambos están en la memoria ejecutable ... ¿Cuándo no es así? ?
Carson Myers
1
Acabo de probar con GCC 4.4, pone cadenas literales en .rodata (datos de solo lectura). Verifiqué con objdump y el listado de ensamblado. No creo que el estándar requiera que las cadenas literales sean de solo lectura, así que creo que incluso podrían colocarse en .data.
Bastien Léonard
Además, no veo ninguna ventaja en no calificar el puntero como constante. Puede ocultar errores si luego decide cambiar la cadena.
Bastien Léonard
29

No, no puede modificarlo, ya que la cadena se puede almacenar en la memoria de solo lectura. Si desea modificarlo, puede usar una matriz en su lugar, por ejemplo,

O alternativamente, puede asignar memoria usando malloc, por ejemplo

Jonathan Maddison
fuente
5
Para completar el código, será bueno si también puede agregar la llamada free ().
Naveen
15

Mucha gente se confunde acerca de la diferencia entre char * y char [] junto con los literales de cadena en C. Cuando escribe:

... en realidad está apuntando a foo a un bloque constante de memoria (de hecho, lo que hace el compilador con "hola mundo" en este caso depende de la implementación).

El uso de char [] en su lugar le dice al compilador que desea crear una matriz y llenarla con el contenido, "hola mundo". foo es el puntero a al primer índice de la matriz char. Ambos son punteros char, pero solo char [] apuntará a un bloque de memoria mutable y asignado localmente.

Jeff Ober
fuente
7

Usted no asigna la memoria para a & b. El compilador es libre de elegir una ubicación de memoria de solo lectura para almacenar los caracteres. Entonces, si intenta cambiarlo, puede resultar en una falla seg. Así que le sugiero que cree una matriz de caracteres usted mismo. Algo como:char a[10]; strcpy(a, "Hello");

Naveen
fuente
1
El problema con las matrices de caracteres es que estoy pasando un puntero de una matriz de caracteres a una función para poder manipular una cadena allí y luego enviarla de nuevo. Parece que tengo que usar malloc por desgracia.
Matthew Stopa
1
No, todavía puede usar el objeto asignado en la pila. Por ejemplo, si tiene una función void f (char * p); luego desde main () puede pasar f (a). Esto pasará la dirección del primer carácter a la función. Además, si decide utilizar malloc (), no olvide liberar la memoria con free ().
Naveen
5

Parece que su pregunta ha sido respondida, pero ahora se preguntará por qué char * a = "String" se almacena en la memoria de solo lectura. Bueno, en realidad no está definido por el estándar c99, pero la mayoría de los compiladores lo eligen de esta manera para casos como:

c99 estándar (pdf) [página 130, sección 6.7.8]:

La declaracion:

define objetos de matriz de caracteres "simples" syt cuyos elementos se inicializan con literales de cadena de caracteres. Esta declaración es idéntica a char

El contenido de las matrices se puede modificar. Por otro lado, la declaración

define p con el tipo "pointer to char" y lo inicializa para apuntar a un objeto con el tipo "array of char" con longitud 4 cuyos elementos se inicializan con una cadena de caracteres literal. Si se intenta utilizar p para modificar el contenido de la matriz, el comportamiento no está definido.

Sweeney
fuente
4

También puede utilizar strdup:

Por tu ejemplo:

Maxime Chéramy
fuente
No es la respuesta a la pregunta, pero sigue siendo una función muy útil, ¡gracias!
mknaf
1
+1 por enseñarme sobre eso strdup. Sin embargo, no estoy seguro de cuándo querría usarlo.
Bosón Z
Cuando haces algo como var = malloc(strlen(str) + 1); strcpy(var, str);, probablemente debas usar strdupen su lugar.
Maxime Chéramy
3

Todas son buenas respuestas que explican por qué no puede modificar los literales de cadena porque se colocan en la memoria de solo lectura. Sin embargo, cuando las cosas se ponen difíciles, hay una manera de hacerlo. Mira este ejemplo:

He escrito esto como parte de mis pensamientos algo más profundos sobre la corrección de constantes , lo que puede resultarle interesante (espero :)).

Espero eso ayude. ¡Buena suerte!


fuente
Tenga en cuenta que cambiar un literal de cadena es un comportamiento indefinido.
Steohan
0

Debe copiar la cadena en otro búfer de memoria que no sea de solo lectura y modificarlo allí. Utilice strncpy () para copiar la cadena, strlen () para detectar la longitud de la cadena, malloc () y free () para asignar dinámicamente un búfer para la nueva cadena.

Por ejemplo (C ++ como pseudocódigo):

diente filoso
fuente
0
Nathan Fellman
fuente
6
El malloc necesita 1 byte más. No olvide el carácter de terminación NULL, que strcpy espera, y también copiará. Este es un error demasiado frecuente.
xcramps