¿Por qué aparece un error de segmentación al escribir en un "char *s" inicializado con una cadena literal, pero no en "char s[]"?

Resuelto Markus asked hace 16 años • 20 respuestas

El siguiente código recibe un error de segmento en la línea 2:

char *str = "string";
str[0] = 'z';  // could be also written as *str = 'z'
printf("%s\n", str);

Si bien esto funciona perfectamente bien:

char str[] = "string";
str[0] = 'z';
printf("%s\n", str);

Probado con MSVC y GCC.

Markus avatar Oct 03 '08 02:10 Markus
Aceptado

Consulte las preguntas frecuentes de C, pregunta 1.32.

P : ¿Cuál es la diferencia entre estas inicializaciones?
char a[] = "string literal";
char *p = "string literal";
Mi programa falla si intento asignar un nuevo valor a p[i].

R : Un literal de cadena (el término formal para una cadena entre comillas dobles en el código fuente C) se puede utilizar de dos maneras ligeramente diferentes:

  1. Como inicializador de una matriz de caracteres, como en la declaración de char a[], especifica los valores iniciales de los caracteres en esa matriz (y, si es necesario, su tamaño).
  2. En cualquier otro lugar, se convierte en una matriz estática de caracteres sin nombre, y esta matriz sin nombre puede almacenarse en una memoria de solo lectura y, por lo tanto, no necesariamente puede modificarse. En un contexto de expresión, la matriz se convierte inmediatamente en un puntero, como es habitual (consulte la sección 6), por lo que la segunda declaración inicializa p para que apunte al primer elemento de la matriz sin nombre.

Algunos compiladores tienen un interruptor que controla si los literales de cadena se pueden escribir o no (para compilar código antiguo), y algunos pueden tener opciones para hacer que los literales de cadena se traten formalmente como matrices de caracteres constantes (para una mejor detección de errores).

matli avatar Oct 02 '2008 19:10 matli

Normalmente, los literales de cadena se almacenan en la memoria de solo lectura cuando se ejecuta el programa. Esto es para evitar que cambie accidentalmente una constante de cadena. En su primer ejemplo, "string"se almacena en la memoria de solo lectura y *strapunta al primer carácter. El error de segmentación ocurre cuando intentas cambiar el primer carácter a 'z'.

"string"En el segundo ejemplo, el compilador copia la cadena desde su inicio de solo lectura a la str[]matriz. Entonces se permite cambiar el primer carácter. Puedes comprobarlo imprimiendo la dirección de cada uno:

printf("%p", str);

Además, imprimir el tamaño de stren el segundo ejemplo le mostrará que el compilador le ha asignado 7 bytes:

printf("%d", sizeof(str));
Greg Hewgill avatar Oct 02 '2008 19:10 Greg Hewgill

La mayoría de estas respuestas son correctas, pero solo para agregar un poco más de claridad...

La "memoria de sólo lectura" a la que la gente se refiere es el segmento de texto en términos ASM. Es el mismo lugar de la memoria donde se cargan las instrucciones. Esto es de sólo lectura por razones obvias como la seguridad. Cuando crea un char* inicializado en una cadena, los datos de la cadena se compilan en el segmento de texto y el programa inicializa el puntero para que apunte al segmento de texto. Así que si intentas cambiarlo, ¡boom! Error de segmentación.

Cuando se escribe como una matriz, el compilador coloca los datos de cadena inicializados en el segmento de datos, que es el mismo lugar donde se encuentran las variables globales y demás. Esta memoria es mutable, ya que no hay instrucciones en el segmento de datos. Esta vez, cuando el compilador inicializa la matriz de caracteres (que todavía es solo un carácter*), apunta al segmento de datos en lugar del segmento de texto, que puede modificar de forma segura en tiempo de ejecución.

Bob Somers avatar Oct 03 '2008 10:10 Bob Somers