¿Cuál es la diferencia entre char array y char pointer en C?
C99 N1256 draft
Hay dos usos diferentes de los literales de cadena de caracteres:
Inicializar char[]
:
char c[] = "abc";
Esto es "más mágico", y se describe en 6.7.8 / 14 "Inicialización":
Una matriz de tipo de caracteres puede ser inicializada por un literal de cadena de caracteres, opcionalmente encerrado entre llaves. Los caracteres sucesivos del literal de 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.
Entonces esto es solo un atajo para:
char c[] = {'a', 'b', 'c', '\0'};
Al igual que cualquier otra matriz regular, c
se puede modificar.
En todas partes: genera un:
Entonces 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[]
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":
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 una cadena literal o literales. La secuencia de caracteres multibyte se usa luego para inicializar una matriz de duración y longitud de almacenamiento estático solo suficiente para contener la secuencia. Para los literales de cadena de caracteres, los elementos de la matriz tienen el 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 apropiados. 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 objetos de matriz de caracteres "simples" s
yt
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.
Implementación de GCC 4.8 x86-64 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 lo almacena char*
en .rodata
sección, 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 (en relación con %rbp
).
Sin embargo , tenga en cuenta que la secuencia de comandos del vinculador predeterminado coloca .rodata
y .text
en el mismo segmento, que tiene permiso de ejecución pero no de escritura. Esto se puede observar con:
readelf -l a.out
que contiene:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
char p[3] = "hello";
la cadena del inicializador es demasiado larga para el tamaño de la matriz que declara. ¿Error de tipografía?