¿Es posible modificar una cadena de caracteres en C?

7 minutos de lectura

avatar de usuario
Mateo Stopa

He estado luchando durante algunas horas con todo tipo de tutoriales de C y libros relacionados con 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:

char *a = "This is a string";
char *b = "new string";

a[2] = b[1]; // Causes a segment fault

*b[2] = b[1]; // This almost seems like it would work but the compiler throws an error.

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

avatar de usuario
Carson Myers

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 en ellas). Cuando escribes char *a = "This is a string"la ubicación de “Esto es una cadena” está en el ejecutable, y la ubicación a apunta a, 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 de la pila.

char a[] = "This is a string";

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

char *a = malloc(256);
strcpy(a, "This is a string");

Siempre que asigne espacio usando malloc() recuerda llamar free() cuando haya terminado con él (léase: pérdida de memoria).

Básicamente, debe realizar un seguimiento de dónde están sus datos. Cada vez que escribe una cadena en su fuente, esa cadena es de solo lectura (de lo contrario, podría cambiar potencialmente el comportamiento del ejecutable; imagine si escribiera char *a = "hello"; y luego cambió a[0] para 'c'. Luego en otro lugar escribió printf("hello");. Si se le permitiera cambiar el primer carácter de "hello"y su compilador solo lo almacenó una vez (debería), luego printf("hello"); daría salida cello!)

  • La última sección me explicó mucho sobre por qué eso necesita ser de solo lectura. Gracias.

    – CDR

    18 de junio de 2009 a las 9:07

  • -1: no indica usar const char*, y nada garantiza que las cadenas literales se almacenen en la memoria ejecutable.

    – Bastien Leonard

    18 de junio de 2009 a las 9:42

  • No, no necesita const para las dos soluciones que he dado, además, si la cadena se conoce en el momento de la compilación y se compila en el ejecutable, ¿dónde más se almacenaría? En gcc, si escribo char *a = “hola.”; o carácter b[] = “hola.”;, luego el ensamblado genera “LC0: .ascii “Hola.\0” LC1: .ascii “Hola.\0″” ambos están en la memoria ejecutable… ¿Cuándo no lo está?

    –Carson Myers

    18 de junio de 2009 a las 18:33

  • Acabo de probar con GCC 4.4, pone cadenas literales en .rodata (datos de solo lectura). Verifiqué con objdump y la lista de ensamblados. No creo que el estándar requiere cadenas literales para ser de solo lectura, por lo que creo que incluso podrían colocarse en .data.

    – Bastien Leonard

    19 de junio de 2009 a las 13:35

  • Además, no veo ninguna ventaja en no calificar el puntero como const. Puede ocultar errores si luego decide cambiar la cadena.

    – Bastien Leonard

    19 de junio de 2009 a las 13:36

avatar de usuario
jonathan madison

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

char a[] = "This is a string";

O alternativamente, puede asignar memoria usando malloc, por ejemplo

char *a = malloc(100);
strcpy(a, "This is a string");
free(a); // deallocate memory once you've done

  • Para completar el código, sería bueno si también puede agregar la llamada gratuita ().

    – Naveen

    18 de junio de 2009 a las 8:47

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

char *foo = "hello world";

… en realidad estás 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).

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

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 de segmento. Así que te sugiero que crees una matriz de caracteres tú mismo. Algo como: char a[10]; strcpy(a, "Hello");

Parece que su pregunta ha sido respondida, pero ahora puede preguntarse 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 instancias como:

printf("Hello, World\n");

estándar c99 (pdf) [page 130, section 6.7.8]:

La declaracion:

char s[] = "abc", t[3] = "abc";

define los objetos de matriz de caracteres “simples” s y t cuyos elementos se inicializan con literales de cadena de caracteres. Esta declaración es idéntica a char

s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

El contenido de las matrices es modificable. Por otra parte, la declaración

char *p = "abc";

define p con tipo “puntero a char” y lo inicializa para que apunte a un objeto con tipo “matriz de char” con longitud 4 cuyos elementos se inicializan con una cadena de caracteres literal. Si se intenta usar p para modificar el contenido de la matriz, el comportamiento no está definido.

avatar de usuario
Maxime Cheramy

También podrías usar strdup:

   The strdup() function returns a pointer to a new string which is a duplicate of the string  s.
   Memory for the new string is obtained with malloc(3), and can be freed with free(3).

Para ti ejemplo:

char *a = strdup("stack overflow");

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 se trata de empujar, hay una manera de hacer esto. Mira este ejemplo:

#include <sys/mman.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int take_me_back_to_DOS_times(const void *ptr, size_t len);

int main()
{
    const *data = "Bender is always sober.";
    printf("Before: %s\n", data);
    if (take_me_back_to_DOS_times(data, sizeof(data)) != 0)
        perror("Time machine appears to be broken!");
    memcpy((char *)data + 17, "drunk!", 6);
    printf("After: %s\n", data);

    return 0;
}

int take_me_back_to_DOS_times(const void *ptr, size_t len)
{
    int pagesize;
    unsigned long long pg_off;
    void *page;

    pagesize = sysconf(_SC_PAGE_SIZE);
    if (pagesize < 0)
        return -1;
    pg_off = (unsigned long long)ptr % (unsigned long long)pagesize;
    page = ((char *)ptr - pg_off);
    if (mprotect(page, len + pg_off, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
        return -1;
    return 0;
}

He escrito esto como parte de mis pensamientos algo más profundos sobre la corrección de constantesque te puede resultar interesante (espero :)).

Espero eso ayude. ¡Buena suerte!

  • Tenga en cuenta que cambiar un literal de cadena es un comportamiento indefinido.

    – Steohan

    20 de abril de 2020 a las 12:07

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad