Qual é a diferença entre char array vs char pointer em C?
C99 N1256 draft
Existem dois usos diferentes dos literais da cadeia de caracteres:
Inicialize char[]
:
char c[] = "abc";
Isso é "mais mágico" e descrito em 6.7.8 / 14 "Inicialização":
Uma matriz do tipo de caractere pode ser inicializada por uma cadeia de caracteres literal, opcionalmente entre chaves. Caracteres sucessivos da literal da cadeia de caracteres (incluindo o caractere nulo final, se houver espaço ou se a matriz for de tamanho desconhecido) inicializam os elementos da matriz.
Portanto, este é apenas um atalho para:
char c[] = {'a', 'b', 'c', '\0'};
Como qualquer outra matriz regular, c
pode ser modificado.
Em qualquer outro lugar: gera um:
Então, quando você escreve:
char *c = "abc";
Isso é semelhante a:
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
Observe a conversão implícita de char[]
para char *
, que é sempre legal.
Então, se você modificar c[0]
, também modifique __unnamed
, que é UB.
Isso está documentado em 6.4.5 "String literals":
5 Na fase de conversão 7, um byte ou código de valor zero é anexado a cada sequência de caracteres multibyte que resulta de uma string literal ou literal. A sequência de caracteres multibyte é então usada para inicializar uma matriz de duração e comprimento estáticos de armazenamento apenas o suficiente para conter a sequência. Para literais da cadeia de caracteres, os elementos da matriz têm o tipo char e são inicializados com os bytes individuais da sequência de caracteres multibyte [...]
6 Não é especificado se essas matrizes são distintas, desde que seus elementos tenham os valores apropriados. Se o programa tentar modificar essa matriz, o comportamento será indefinido.
6.7.8 / 32 "Inicialização" dá um exemplo direto:
EXEMPLO 8: A declaração
char s[] = "abc", t[3] = "abc";
define objetos de matriz de caracteres "simples" s
e t
cujos elementos são inicializados com literais de cadeia de caracteres.
Esta declaração é idêntica à
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
O conteúdo das matrizes é modificável. Por outro lado, a declaração
char *p = "abc";
define p
com o tipo "ponteiro para char" e o inicializa para apontar para um objeto com o tipo "array of char" com comprimento 4 cujos elementos são inicializados com uma literal de cadeia de caracteres. Se for feita uma tentativa p
de modificar o conteúdo da matriz, o comportamento será indefinido.
Implementação do GCC 4.8 x86-64 ELF
Programa:
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Compilar e descompilar:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
A saída contém:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Conclusão: o GCC o armazena char*
em .rodata
seção, não em .text
.
Se fizermos o mesmo para char[]
:
char s[] = "abc";
nós obtemos:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
para que seja armazenado na pilha (em relação a %rbp
).
Observe, no entanto, que o script do vinculador padrão coloca .rodata
e .text
no mesmo segmento, que tem execução, mas não possui permissão de gravação. Isso pode ser observado com:
readelf -l a.out
que contém:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
char p[3] = "hello";
a cadeia de inicialização é muito longa para o tamanho da matriz que você declara. Erro de digitação?