Qual'è la differenza tra char array e char pointer in C?
Bozza C99 N1256
Esistono due diversi usi dei letterali stringa di caratteri:
Inizializza char[]:
char c[] = "abc";
Questo è "più magico", e descritto al 6.7.8 / 14 "Inizializzazione":
Una matrice di tipo di carattere può essere inizializzata da una stringa di caratteri letterale, facoltativamente racchiusa tra parentesi graffe. I caratteri successivi della stringa di caratteri letterali (incluso il carattere null di terminazione se c'è spazio o se la matrice ha dimensioni sconosciute) inizializzano gli elementi della matrice.
Quindi questa è solo una scorciatoia per:
char c[] = {'a', 'b', 'c', '\0'};
Come qualsiasi altro array normale, cpuò essere modificato.
Ovunque: genera un:
Quindi quando scrivi:
char *c = "abc";
Questo è simile a:
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
Nota il cast implicito da char[]a char *, che è sempre legale.
Quindi, se si modifica c[0], si modifica anche __unnamed, che è UB.
Questo è documentato in 6.4.5 "String letterals":
5 Nella fase di traduzione 7, un byte o un codice di valore zero viene aggiunto a ciascuna sequenza di caratteri multibyte risultante da una stringa letterale o letterale. La sequenza di caratteri multibyte viene quindi utilizzata per inizializzare una matrice di durata e lunghezza della memoria statica appena sufficiente per contenere la sequenza. Per i letterali stringa di caratteri, gli elementi dell'array hanno tipo char e vengono inizializzati con i singoli byte della sequenza di caratteri multibyte [...]
6 Non è specificato se questi array siano distinti, purché i loro elementi abbiano i valori appropriati. Se il programma tenta di modificare un tale array, il comportamento non è definito.
6.7.8 / 32 "Inizializzazione" fornisce un esempio diretto:
ESEMPIO 8: La dichiarazione
char s[] = "abc", t[3] = "abc";
definisce oggetti "semplici" di array di caratteri se i tcui elementi sono inizializzati con valori letterali di stringa di caratteri.
Questa dichiarazione è identica a
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Il contenuto degli array è modificabile. D'altra parte, la dichiarazione
char *p = "abc";
definisce pcon il tipo "pointer to char" e lo inizializza in modo che punti a un oggetto con tipo "array of char" con lunghezza 4 i cui elementi sono inizializzati con una stringa di caratteri letterale. Se si tenta di utilizzare pper modificare il contenuto dell'array, il comportamento non è definito.
Implementazione ELF GCC 4.8 x86-64
Programma:
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Compilare e decompilare:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
L'output contiene:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Conclusione: GCC lo memorizza char*nella .rodatasezione, non in .text.
Se facciamo lo stesso per char[]:
char s[] = "abc";
otteniamo:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
quindi viene memorizzato nello stack (rispetto a %rbp ).
Si noti tuttavia che lo script del linker predefinito inserisce .rodatae si trova .textnello stesso segmento, che ha l'esecuzione ma nessuna autorizzazione di scrittura. Questo può essere osservato con:
readelf -l a.out
che contiene:
Section to Segment mapping:
Segment Sections...
02 .text .rodata
char p[3] = "hello";la stringa di inizializzazione è troppo lunga per la dimensione dell'array dichiarata. Errore di battitura?