El siguiente código recibe una falla seg 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.
c
segmentation-fault
c-strings
Markus
fuente
fuente
Respuestas:
Consulte las preguntas frecuentes de C, pregunta 1.32
fuente
mprotect
de protección de solo lectura (ver aquí ).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*str
apunta al primer carácter. El segfault ocurre cuando intenta cambiar el primer carácter a'z'
.En el segundo ejemplo, la cadena
"string"
está copiado por el compilador de su hogar de sólo lectura a lastr[]
matriz. Luego se permite cambiar el primer carácter. Puede verificar esto imprimiendo la dirección de cada uno:Además, imprimir el tamaño de
str
en el segundo ejemplo le mostrará que el compilador le ha asignado 7 bytes:fuente
%zu
para imprimirsize_t
La mayoría de estas respuestas son correctas, pero solo para agregar un poco más de claridad ...
La "memoria de solo lectura" a la que se refieren las personas es el segmento de texto en términos de ASM. Es el mismo lugar en la memoria donde se cargan las instrucciones. Esto es de solo 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 apuntar al segmento de texto. Entonces, si intentas cambiarlo, kaboom. Segfault.
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 viven sus variables globales y tales. 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.
fuente
C99 N1256 draft
Hay dos usos diferentes de los literales de cadena de caracteres:
Inicializar
char[]
:Esto es "más mágico", y se describe en 6.7.8 / 14 "Inicialización":
Entonces esto es solo un atajo para:
Al igual que cualquier otra matriz regular,
c
se puede modificar.En todas partes: genera un:
Entonces cuando escribes:
Esto es similar a:
Tenga en cuenta la conversión implícita de
char[]
achar *
, que siempre es legal.Luego, si modifica
c[0]
, también modifica__unnamed
, que es UB.Esto se documenta en 6.4.5 "Literales de cadena":
6.7.8 / 32 "Inicialización" da un ejemplo directo:
Implementación de GCC 4.8 x86-64 ELF
Programa:
Compilar y descompilar:
La salida contiene:
Conclusión: GCC lo almacena
char*
en.rodata
sección, no en.text
.Si hacemos lo mismo para
char[]
:obtenemos:
por lo que se almacena en la pila (en relación con
%rbp
).Sin embargo , tenga en cuenta que la secuencia de comandos del enlazador predeterminado coloca
.rodata
y.text
en el mismo segmento, que tiene permiso de ejecución pero no escritura. Esto se puede observar con:que contiene:
fuente
En el primer código, "cadena" es una constante de cadena, y las constantes de cadena nunca deben modificarse porque a menudo se colocan en la memoria de solo lectura. "str" es un puntero que se utiliza para modificar la constante.
En el segundo código, "cadena" es un inicializador de matriz, una especie de abreviatura para
"str" es una matriz asignada en la pila y se puede modificar libremente.
fuente
str
es global ostatic
.Porque el tipo de
"whatever"
en el contexto del primer ejemplo esconst char *
(incluso si lo asigna a un carácter no constante *), lo que significa que no debe intentar escribir en él.El compilador ha forzado esto colocando la cadena en una parte de solo lectura de la memoria, por lo tanto, escribir en ella genera un defecto de seguridad.
fuente
Para comprender este error o problema, primero debe conocer la diferencia b / w del puntero y la matriz, por lo que aquí, primero, debo explicarle las diferencias b / w
conjunto de cadenas
En la matriz de memoria se almacena en celdas de memoria continua, almacenada como
[h][e][l][l][o][\0] =>[]
una celda de memoria de 1 byte de tamaño, y a estas celdas de memoria continua se puede acceder por nombre llamado strarray aquí. Así que aquí la matriz de cadenasstrarray
contiene todos los caracteres de cadena inicializados en ella. caso aquí"hello"
para que podamos cambiar fácilmente su contenido de memoria accediendo a cada carácter por su valor de índicey su valor cambió a
'm'
tan strarray valor cambiado a"mello"
;Un punto a tener en cuenta aquí es que podemos cambiar el contenido de la matriz de cadenas cambiando carácter por carácter, pero no podemos inicializar otra cadena directamente como si
strarray="new string"
no fuera válido.Puntero
Como todos sabemos, el puntero apunta a la ubicación de la memoria en la memoria, el puntero no inicializado apunta a una ubicación de memoria aleatoria, y después de la inicialización apunta a una ubicación de memoria particular
aquí el puntero ptr se inicializa en una cadena
"hello"
que es una cadena constante almacenada en la memoria de solo lectura (ROM), por"hello"
lo que no se puede cambiar ya que está almacenada en la ROMy ptr se almacena en la sección de pila y apunta a una cadena constante
"hello"
entonces ptr [0] = 'm' no es válido ya que no puede acceder a la memoria de solo lectura
Pero ptr se puede inicializar a otro valor de cadena directamente ya que es solo un puntero, por lo que puede apuntar a cualquier dirección de memoria de variable de su tipo de datos
fuente
Lo anterior establece
str
que apunta al valor literal"string"
que está codificado en la imagen binaria del programa, que probablemente está marcado como de solo lectura en la memoria.Entonces
str[0]=
está intentando escribir en el código de solo lectura de la aplicación. Sin embargo, supongo que esto probablemente depende del compilador.fuente
asigna un puntero a un literal de cadena, que el compilador está colocando en una parte no modificable de su ejecutable;
asigna e inicializa una matriz local que es modificable
fuente
int *b = {1,2,3)
como escribimoschar *s = "HelloWorld"
?Las preguntas frecuentes de C a las que @matli lo vinculó lo mencionan, pero nadie más lo ha hecho todavía, así que para aclaración: si una cadena literal (cadena de comillas dobles en su fuente) se usa en cualquier otro lugar que no sea para inicializar una matriz de caracteres (es decir: @ El segundo ejemplo de Mark, que funciona correctamente), esa cadena es almacenada por el compilador en una tabla especial de cadenas estáticas , que es similar a crear una variable estática global (solo lectura, por supuesto) que es esencialmente anónima (no tiene nombre de variable) "). La parte de solo lectura es la parte importante, y es la razón por la cual el primer ejemplo de código de @ Mark es predeterminado.
fuente
int *b = {1,2,3)
como escribimoschar *s = "HelloWorld"
?los
La línea define un puntero y lo apunta a una cadena literal. La cadena literal no se puede escribir, así que cuando lo haga:
Tienes una falla seg. En algunas plataformas, el literal puede estar en la memoria de escritura, por lo que no verá un segfault, pero es un código no válido (que resulta en un comportamiento indefinido) independientemente.
La línea:
asigna una matriz de caracteres y copia la cadena literal en esa matriz, que es completamente grabable, por lo que la actualización posterior no es un problema.
fuente
int *b = {1,2,3)
como escribimoschar *s = "HelloWorld"
?Los literales de cadena como "cadena" probablemente se asignan en el espacio de direcciones de su ejecutable como datos de solo lectura (más o menos su compilador). Cuando vas a tocarlo, se asusta de que estés en el área de su traje de baño y te avisa con una falla seg.
En su primer ejemplo, obtendrá un puntero a esos datos constantes. En su segundo ejemplo, está inicializando una matriz de 7 caracteres con una copia de los datos constantes.
fuente
fuente
En primer lugar,
str
es un puntero que apunta a"string"
. El compilador puede colocar literales de cadena en lugares en la memoria en los que no puede escribir, pero solo puede leer. (Esto realmente debería haber activado una advertencia, ya que está asignando unaconst char *
a unachar *
. ¿Tenía las advertencias desactivadas o simplemente las ignoró?)En segundo lugar, está creando una matriz, que es la memoria a la que tiene acceso completo, e inicializándola
"string"
. Estás creando unchar[7]
(seis para las letras, uno para la terminación '\ 0'), y haces lo que quieras con él.fuente
const
prefijo make variables Solochar [N]
, noconst char [N]
, por lo que no hay advertencia. (Puede cambiar eso en gcc al menos pasando-Wwrite-strings
).Supongamos que las cuerdas son,
En el primer caso, el literal debe copiarse cuando 'a' entra en el alcance. Aquí 'a' es una matriz definida en la pila. Significa que la cadena se creará en la pila y sus datos se copian de la memoria de código (texto), que generalmente es de solo lectura (esto es específico de la implementación, un compilador puede colocar estos datos del programa de solo lectura en la memoria de lectura y escritura también )
En el segundo caso, p es un puntero definido en la pila (ámbito local) y que hace referencia a un literal de cadena (datos de programa o texto) almacenado en otro lugar donde. Por lo general, modificar dicha memoria no es una buena práctica ni se recomienda.
fuente
Primero hay una cadena constante que no se puede modificar. El segundo es una matriz con valor inicializado, por lo que se puede modificar.
fuente
La falla de segmentación se produce cuando intenta acceder a la memoria que es inaccesible.
char *str
es un puntero a una cadena que no se puede modificar (la razón para obtener la segfault).mientras que
char str[]
es una matriz y puede ser modificable.fuente