Perché questa semplice assegnazione di stringhe segfault?
-
03-07-2019 - |
Domanda
I & # 8217; ho il seguente codice:
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char* b = "bar";
a = b;
cout << a << ", " << b << endl;
return 0;
}
Questo compila e funziona, cioè. stampa bar,
bar
. Ora vorrei dimostrare che ciò che accade qui non è copiare una stringa. Vorrei cambiare b
e mostrare che a
cambia anche. Ho trovato questo semplice codice:
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char* b = "bar";
a = b;
b[1] = 'u'; // ← just this line added
cout << a << ", " << b << endl;
return 0;
}
& # 8230; ma segfault. Perché? La cosa interessante è che la seguente modifica funziona bene:
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char b[] = "bar"; // ← declaration changed here
a = b;
b[1] = 'u';
cout << a << ", " << b << endl;
return 0;
}
Perché non & # 8217; t segfault come il precedente? Immagino che mi manchi qualche differenza importante tra l'inizializzazione della stringa in stile puntatore e quella in stile array.
Soluzione
Non puoi cambiare le costanti di stringa, che è ciò che ottieni quando usi la sintassi da puntatore a letterale come nei primi esempi di codice.
Vedi anche questa domanda: È una stringa letterale in c ++ creato nella memoria statica? .
Altri suggerimenti
Quando scrivi questo:
char *b = "bar";
il compilatore alloca un'area di memoria anonima (senza nome) per memorizzare la stringa letterale " bar " ;. I valori letterali di stringa non possono essere modificati, quindi il compilatore (con l'aiuto del linker e del sistema operativo) inserisce il valore letterale di stringa in una parte dello spazio di memoria del programma in esecuzione che è protetto da scrittura. Quando si tenta di modificarlo, il sistema operativo lo rileva e provoca un errore di segmentazione del programma.
(Il tuo codice è C ++, non C, ma è irrilevante per questa domanda.)
Quando scrivi:
char *foo = "bar";
Ciò che realmente accade è che " bar " è memorizzato nel segmento di sola lettura della memoria. Pertanto, è immutabile. Ottieni un segfault perché provi a modificare un segmento di sola lettura.
Puoi anche mostrare che 'a' è stato modificato stampando il valore del puntatore.
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char* b = "bar";
a = b;
cout << (void*)a << ", " << (void*)b << endl;
}
Questo stamperà l'indirizzo a cui 'a' e 'b' indicano.
Devi lanciare 'void *' perché l'operatore & Lt; & Lt; è sovraccarico per 'char *' per stampare la stringa che qualsiasi altro puntatore stamperà l'indirizzo.
In teoria, una stringa letterale non dovrebbe essere in grado di essere assegnata a un carattere *, ma solo un 'carattere costante *'. Quindi il compilatore ti fermerebbe prima di scrivere il codice di errore seg.
Questa differenza è forse specifica del compilatore. Per dimostrare il tuo punto usa malloc per allocare il buffer, quindi copia la stringa in questo buffer e non dimenticare di usare gratuitamente quando non hai più bisogno della stringa.