2012-04-22 7 views
13

So che ad esempio "hello" è di tipo const char*. Quindi le mie domande sono:Perché è possibile assegnare un const char * a un char *?

  1. Come possiamo assegnare una stringa letterale come "hello" ad un non const char* come questo:

    char* s = "hello"; // "hello" is type of const char* and s is char* 
            // and we know that conversion from const char* to 
            // char* is invalid 
    
  2. È una stringa letterale come "hello", che avrà la memoria in tutta la il mio programma, o è proprio come una variabile temporanea che verrà distrutta al termine dell'istruzione?

+2

Questo è consentito solo per la compatibilità con C. Generalmente in C++ è considerato deprecato e i buoni compilatori danno un avvertimento. – iammilind

risposta

22

Infatti, "hello" è di tipo char const[6].

Ma il succo della domanda è ancora corretto: perché il C++ ci consente di assegnare una posizione di memoria di sola lettura a un tipo non const?

L'unico motivo è la compatibilità con le versioni precedenti con il vecchio codice C, che non conosceva const. Se C++ fosse stato rigoroso qui avrebbe rotto un sacco di codice esistente.

Detto questo, la maggior parte dei compilatori può essere configurata su con avviso su tale codice come deprecato, o addirittura farlo in modo predefinito. Inoltre, C++ 11 non consente questo, ma i compilatori potrebbero non applicarlo ancora.


For Fans Standerdese:
[Ref 1]C++ 03 standard: §4.2/2

Una stringa letterale (2.13.4) che non è una grande stringa letterale può essere convertita in un valore di rvalore di tipo "puntatore al carattere"; un letterale di stringa ampio può essere convertito in un valore di rvalore di tipo "puntatore a wchar_t". In entrambi i casi, il risultato è un puntatore al primo elemento dell'array. Questa conversione viene considerata solo quando esiste un tipo di target puntatore esplicito appropriato e non quando esiste una necessità generale di conversione da un valore lvalue a un valore rvalue. [Nota: questa conversione è obsoleta. Vedi Allegato D.] Ai fini del ranking nella risoluzione di sovraccarico (13.3.3.1.1), questa conversione è considerata una conversione da matrice a puntatore seguita da una conversione di qualifica (4.4). [Esempio: "abc" viene convertito in "puntatore a const char" come conversione da matrice a puntatore e quindi a "puntatore a char" come conversione di qualifica. ]

C++ 11 rimuove semplicemente la citazione sopra che implica che è un codice illegale in C++ 11.

[Ref 2]C99 normali 6.4.5/5 "letterali di stringa - Semantics":

In fase di traduzione 7, un byte o codice di valore zero è allegato a ciascun carattere multibyte sequenza risultante da una stringa letterale o letterale. La sequenza di caratteri multibyte viene quindi utilizzata per inizializzare un array di durata e durata di memorizzazione statica sufficiente per contenere la sequenza.Per i letterali stringa di caratteri, gli elementi dell'array hanno carattere char e sono inizializzati con i singoli byte della sequenza di caratteri multibyte; per i letterali a stringa ampia, gli elementi dell'array hanno tipo wchar_t e sono inizializzati con la sequenza di caratteri ampi ...

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.

+1

Aggiunte le citazioni pertinenti dallo standard. Quindi non vi dispiacerà. –

+0

MSVC in modalità di debug ti impedirà di scrivere su una stringa letterale in fase di esecuzione, GCC in fase di compilazione (se possibile). Tuttavia, c'è un'opzione, credo che sia -wwrite-stringhe o tale che attiva la modalità di compatibilità e mette letterali stringa nella memoria R/W –

+2

@Als Hai preso una cosa semplice e l'hai resa brutta. ;-) Ma grazie.In realtà volevo aggiungere anche questo, ma non riesco a trovare i passaggi rilevanti nella mia bozza di C++ 11. 2.14.3 definisce il tipo e l'allegato specifica l'invalidità della conversione, ma nulla mostra che questo sia stato deprecato ma valido in C++ 03. –

1

Basta usare un string:

std::string s("hello"); 

Questo sarebbe il modo C++. Se devi davvero usare char, dovrai creare una matrice e copiarne il contenuto.

2

è una stringa letterale come "ciao" prenderà memoria in tutto il mio programma tutto è proprio come una variabile temporanea che verrà distrutta al termine dell'istruzione.

Viene mantenuto nei dati del programma, quindi è attendibile per tutta la durata del programma. È possibile restituire puntatori e riferimenti a questi dati dall'ambito corrente.

L'unico motivo per cui const char* viene trasmesso su char* è compatibile con c, come le chiamate di sistema winapi. E questo cast è reso non esplicito a differenza di qualsiasi altro casting const.

+0

quindi const char * è l'unico tipo che può essere convertito in char * in modo non esplicito dal compilatore. – AlexDan

+0

AlexDan, 'const char *' è l'unico tipo che può essere convertito nella sua forma non const (per esempio, 'const int *' non può essere lanciato su 'int *' in modo non esplicito). Ma puoi regolare il compilatore per generare un waring. –

+2

inoltre, * cast * è un verbo irregolare, 3 forme: * cast-cast-cast * –

-3

Nel tuo esempio, non stai assegnando, ma costruendo. std :: string, ad esempio, ha un costruttore std::string(const char *) (in realtà è più complicato, ma non importa). Analogamente, char * (se fosse un tipo piuttosto che un puntatore a un tipo) potrebbe avere un costruttore const char *, che copia la memoria.

In realtà non so come funzioni realmente il compilatore qui, ma penso che potrebbe essere simile a quello che ho descritto sopra: una copia di "Hello" è costruita nello stack e s viene inizializzata con l'indirizzo di questa copia.

+2

Questo semplicemente non è corretto. Non viene eseguita alcuna copia e la modifica della stringa tramite il puntatore è UB. –

+0

Non c'è copia di "Hello" nello stack! Si tratta di una stringa letterale che verrà creata in alcuni [segmenti di dati] globali (https://secure.wikimedia.org/wikipedia/en/wiki/Data_segment) (ma questo è un dettaglio specifico dell'implementazione). – Praetorian

+0

@KonradRudolph, non ho mai detto che fosse corretto. Sono triste, potrebbe essere così. Grazie per i collegamenti comunque. – Steed

1

La risposta alla seconda domanda è che la variabile s è memorizzata nella RAM come tipo puntatore-a-carattere. Se è globale o statico, viene allocata nell'heap e rimane lì per la durata del programma in esecuzione. Se è una variabile locale ("auto"), viene allocata nello stack e rimane lì fino a quando la funzione corrente non ritorna. In entrambi i casi, occupa la quantità di memoria richiesta per contenere un puntatore.

La stringa "Hello" è una costante ed è memorizzata come parte del programma stesso, insieme a tutte le altre costanti e inizializzatori. Se hai costruito il tuo programma per l'esecuzione su un'appliance, la stringa verrà archiviata nella ROM.

Si noti che, poiché la stringa è costante e s è un puntatore, non è necessaria alcuna copia. Il puntatore s punta semplicemente verso la posizione in cui è memorizzata la stringa.