segmentation error dumped c segmentation-fault c-strings

error - segmentation fault(core dumped) php



¿Por qué aparece un error de segmentación al escribir en una cadena inicializada con "char*s" pero no "char s[]"? (16)

¿Por qué aparece un error de segmentación al escribir en una cadena?

C99 N1256 borrador

Hay dos usos diferentes de los literales de cadena de caracteres:

  1. Inicializar char[] :

    char c[] = "abc";

    Esto es "más magia", y se describe en 6.7.8 / 14 "Inicialización":

    Una matriz de tipo de carácter puede ser inicializada por un literal de cadena de caracteres, opcionalmente encerrado entre llaves. Los caracteres sucesivos del literal de la cadena de caracteres (incluido el carácter nulo de terminación si hay espacio o si la matriz es de tamaño desconocido) inicializan los elementos de la matriz.

    Así que esto es sólo un atajo para:

    char c[] = {''a'', ''b'', ''c'', ''/0''};

    Como cualquier otra matriz regular, c puede ser modificado.

  2. En todas partes: genera un:

    Así que cuando escribes:

    char *c = "abc";

    Esto es similar a:

    /* __unnamed is magic because modifying it gives UB. */ static char __unnamed[] = "abc"; char *c = __unnamed;

    Tenga en cuenta la conversión implícita de char[] a char * , que siempre es legal.

    Luego, si modifica c[0] , también modifica __unnamed , que es UB.

    Esto está documentado en 6.4.5 "literales de cadena":

    5 En la fase de traducción 7, se agrega un byte o código de valor cero a cada secuencia de caracteres multibyte que resulta de un literal de cadena o literales. La secuencia de caracteres multibyte luego se usa para inicializar una matriz de duración y longitud de almacenamiento estático suficiente para contener la secuencia. Para los literales de cadena de caracteres, los elementos de la matriz tienen tipo char, y se inicializan con los bytes individuales de la secuencia de caracteres multibyte [...]

    6 No se especifica si estas matrices son distintas siempre que sus elementos tengan los valores adecuados. Si el programa intenta modificar dicha matriz, el comportamiento es indefinido.

6.7.8 / 32 "Inicialización" da un ejemplo directo:

EJEMPLO 8: La declaración

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

define los 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 s[] = { ''a'', ''b'', ''c'', ''/0'' }, t[] = { ''a'', ''b'', ''c'' };

Los contenidos de las matrices son modificables. Por otro lado, la declaración.

char *p = "abc";

define p con el tipo "puntero a 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.

GCC 4.8 x86-64 Implementación ELF

Programa:

#include <stdio.h> int main(void) { char *s = "abc"; printf("%s/n", s); return 0; }

Compilar y descompilar:

gcc -ggdb -std=c99 -c main.c objdump -Sr main.o

La salida contiene:

char *s = "abc"; 8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp) f: 00 c: R_X86_64_32S .rodata

Conclusión: GCC almacena char* it en la sección .rodata , no en .text .

Si hacemos lo mismo para char[] :

char s[] = "abc";

obtenemos:

17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)

por lo que se almacena en la pila (relativa a %rbp ).

Sin embargo, .text en cuenta que la secuencia de comandos de vinculador predeterminada coloca .rodata y .text en el mismo segmento, que tiene permisos de escritura pero no de ejecución. Esto se puede observar con:

readelf -l a.out

que contiene:

Section to Segment mapping: Segment Sections... 02 .text .rodata

El siguiente código recibe la 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.


Debido a que el tipo de "whatever" en el contexto del primer ejemplo es const char * (incluso si lo asigna a un no const const *), lo que significa que no debe intentar escribir en él.

El compilador ha aplicado esto al colocar la cadena en una parte de la memoria de solo lectura, por lo que escribir en ella genera un segfault.


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 mano corta para

char str[7] = { ''s'', ''t'', ''r'', ''i'', ''n'', ''g'', ''/0'' };

"str" ​​es una matriz asignada en la pila y se puede modificar libremente.


En primer lugar, str es un puntero que apunta a "string" . El compilador tiene permitido poner 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ás asignando un const char * a un char * . ¿Habías desactivado las advertencias, o simplemente las ignoraste?)

En segundo lugar, está creando una matriz, que es la memoria a la que tiene acceso completo, y la inicializa con "string" . Estás creando un char[7] (seis para las letras, uno para la terminación ''/ 0''), y haces lo que quieras con él.


La C Preguntas frecuentes que @matli vinculó para mencionarlo, pero nadie más lo tiene todavía, así que para aclarar: si se utiliza una cadena literal (cadena entre comillas dobles en su fuente) 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 variable "nombre "). La parte de solo lectura es la parte importante, y es por eso que el primer código de @ Mark segfaults.


La falla de segmentación se produce cuando intenta acceder a la memoria que no es accesible.

char *str es un puntero a una cadena que no es modificable (la razón para obtener una falla de seg).

mientras que char str[] es una matriz y puede ser modificable ..


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 creas 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. Así que si intentas cambiarlo, kaboom. Segfault.

Cuando se escribe como una matriz, el compilador coloca en su lugar los datos de la cadena inicializada en el segmento de datos, que es el mismo lugar que sus variables globales y en vivo. 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.


Los literales de cadena como "cadena" probablemente se asignan en el espacio de direcciones de su ejecutable como datos de solo lectura (dé o lleve su compilador). Cuando vas a tocarlo, se asusta que estás en el área de su traje de baño y te lo hace saber con una falla de seguridad.

En tu primer ejemplo, obtienes un puntero a esos datos const. En su segundo ejemplo, está inicializando una matriz de 7 caracteres con una copia de los datos const.


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. La falla de seguridad ocurre cuando intentas cambiar el primer carácter a ''z'' .

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

printf("%p", str);

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

printf("%d", sizeof(str));


Para comprender este error o problema, primero debe saber la diferencia entre el puntero y la matriz, por lo tanto, primero les explico las diferencias entre ellos.

cadena de cadenas

char strarray[] = "hello";

En la matriz de memoria se almacena en celdas de memoria continua, almacenadas como [h][e][l][l][o][/0] =>[] es una celda de memoria de 1 byte, y esta celda de memoria continua puede ser acceso por nombre denominado strarray here.so aquí string strarray matriz que contiene todos los caracteres de string inicializados en este caso. aquí "hello" para que podamos cambiar fácilmente su contenido de memoria accediendo a cada carácter por su valor de índice

`strarray[0]=''m''` it access character at index 0 which is ''h''in strarray

y su valor cambió a ''m'' así que el valor de strarray cambió 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 strarray="new string" no es válida

Puntero

Como todos sabemos, el puntero apunta a la ubicación de la memoria en la memoria, el puntero no inicializado apunta a la ubicación de la memoria al azar, y después de la inicialización señala a la ubicación de la memoria en particular

char *ptr = "hello";

aquí el puntero ptr se inicializa en la cadena "hello" que es una cadena constante almacenada en la memoria de solo lectura (ROM), por lo que "hello" no se puede cambiar ya que está almacenada en la ROM

y ptr se almacena en la sección de pila y apunta a la cadena constante "hello"

así que ptr [0] = ''m'' no es válido porque no puede acceder a la memoria de solo lectura

Pero ptr puede inicializarse directamente a otro valor de cadena, ya que es solo un puntero, por lo que puede apuntar a cualquier dirección de memoria de la variable de su tipo de datos

ptr="new string"; is valid


Primero hay una cadena constante que no puede ser modificada. La segunda es una matriz con valor inicializado, por lo que se puede modificar.


Vea la C Preguntas frecuentes, pregunta 1.32

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

A : Un literal de cadena (el término formal para una cadena entre comillas dobles en la fuente C) se puede usar de dos maneras ligeramente diferentes:

  1. Como inicializador para una matriz de caracteres, como en la declaración de caracteres 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 de caracteres sin nombre, estática, y esta matriz sin nombre se puede almacenar en la memoria de solo lectura, y por lo tanto, no se puede modificar necesariamente. En un contexto de expresión, la matriz se convierte a la vez 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 el código antiguo), y algunos pueden tener opciones para hacer que los literales de cadena se traten formalmente como matrices de caracteres const (para una mejor captura de errores).


los

char *str = "string";

línea define un puntero y lo apunta a una cadena literal. La cadena literal no se puede escribir, así que cuando lo haces:

str[0] = ''z'';

tienes una falla de 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 (lo que resulta en un comportamiento indefinido) independientemente.

La línea:

char str[] = "string";

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.


// create a string constant like this - will be read only char *str_p; str_p = "String constant"; // create an array of characters like this char *arr_p; char arr[] = "String in an array"; arr_p = &arr[0]; // now we try to change a character in the array first, this will work *arr_p = ''E''; // lets try to change the first character of the string contant *str_p = ''G''; // this will result in a segmentation fault. Comment it out to work. /*----------------------------------------------------------------------------- * String constants can''t be modified. A segmentation fault is the result, * because most operating systems will not allow a write * operation on read only memory. *-----------------------------------------------------------------------------*/ //print both strings to see if they have changed printf("%s/n", str_p); //print the string without a variable printf("%s/n", arr_p); //print the string, which is in an array.


char *str = "string";

asigna un puntero a una cadena literal, que el compilador está colocando en una parte no modificable de su ejecutable;

char str[] = "string";

asigna e inicializa una matriz local que es modificable


char *str = "string";

Lo anterior establece str para apuntar al valor literal "string" que está codificado en la imagen binaria del programa, que probablemente está marcado como de solo lectura en la memoria.

Así que str[0]= está intentando escribir en el código de solo lectura de la aplicación. Supongo que esto es probablemente dependiente del compilador sin embargo.