Modifica di una stringa const di char *

So che const char * è un puntatore a un const char, mentre char *const è un puntatore costante a un char. Sto testando questo nel seguente codice:

 const char *s = "hello"; // Not permitted to modify the string "hello" char *const t = "world"; // Not permitted to modify the pointer t s = "hello2"; // Valid // t = "world2"; // Invalid, gives compilation error // *(s + 1) = 'a'; // Invalid, gives compilation error *(t + 1) = 'a'; // Why does this not work? 

L’ultima riga non dà alcun errore, ma causa la chiusura imprevista del programma. Perché la modifica della stringa puntata da t non è consentita?

t sta puntando a una stringa letterale , è un comportamento indefinito modificare un letterale stringa . La sezione standard di draft C ++ 2.14.5 String letterals, paragrafo 12, dice ( enfasi mia ):

Se tutti i valori letterali stringa sono distinti (ovvero vengono memorizzati in oggetti non sovrapposti) viene definita l’implementazione. L’effetto del tentativo di modificare una stringa letterale non è definito .

La sezione pertinente dalla bozza di standard C99 è 6.4.5 String letterals paragraph 6 che dice ( enfasi mia ):

Non è specificato se questi array siano distinti purché i loro elementi abbiano i valori appropriati. Se il programma tenta di modificare tale array, il comportamento non è definito.

Su una tipica piattaforma Unix moderna troverete stringhe letterali nel segmento di sola lettura che comporterebbero una violazione di accesso se tentiamo di modificarlo. Possiamo usare objdump per ispezionare la sezione di sola lettura come segue:

 objdump -s -j .rodata 

possiamo vedere nel seguente esempio dal vivo che la stringa letterale sarà effettivamente trovata nella sezione di sola lettura . Nota che ho dovuto aggiungere un printf altrimenti il ​​compilatore avrebbe ottimizzato la stringa letterale. Esempio di output objdump :

 Contents of section .rodata: 400668 01000200 776f726c 64002573 0a00 ....world.%s.. 

Un approccio alternativo potrebbe essere quello di fare riferimento a una matrice con una copia di una stringa letterale in questo modo:

 char r[] = "world"; char *const t = r ; 

Sebbene i letterali stringa in C abbiano ufficialmente un tipo di char[] (array di char , non const ), lo standard C afferma espressamente che devono essere trattati come non modificabili. I compilatori tendono a inserire letterali stringa in un segmento di sola lettura, quindi il tentativo di modificarli comporta una violazione di accesso.

I valori letterali delle stringhe sono descritti nella sezione 6.4.5 della norma C11 (ISO / IEC 9899: 2011).

È ansible ignorare l’errore del compilatore eseguendolo nuovamente come char* , come in *((char*)s + 1) = 'a'; ma poiché era già presente in altre risposte, si tratta di un comportamento indefinito e probabilmente causerà un errore di segmentazione perché si sta modificando un letterale stringa.

Se si desidera testarlo correttamente, inizializzare le stringhe in una funzione in modo che l’inizializzazione possa essere dynamic e utilizzare strdup() per quello.

 int main(int argc, char **argv) { char *d1 = strdup("hello"); char *d2 = strdup("world"); const char *s = d1; char *const t = d2; ... free(d1); free(d2); } 

Le variabili d1 e d2 vengono utilizzate principalmente in modo che le allocazioni dinamiche possano essere correttamente liberate utilizzando free() alla fine. Inoltre, come suggerito da altre risposte, considera sempre i valori letterali stringa come const char * .