conversione obsoleta dalla costante di stringa in 'char *'


16

Cosa significa questo errore? Non posso risolverlo in alcun modo.

avviso: conversione obsoleta dalla costante di stringa in 'char *' [-Wwrite-stringhe]


Questa domanda dovrebbe essere su StackOverflow, non su Arduino :)
Vijay Chavda,

Risposte:


26

Come al solito, fornirò un po 'di informazioni tecniche di base sui perché e sui motivi di questo errore.

Esaminerò quattro modi diversi di inizializzare le stringhe C e vedremo quali sono le differenze tra loro. Questi sono i quattro modi in questione:

char *text = "This is some text";
char text[] = "This is some text";
const char *text = "This is some text";
const char text[] = "This is some text";

Ora per questo voglio cambiare la terza lettera "i" in una "o" per renderla "Thos is some text". Ciò potrebbe, in tutti i casi (si potrebbe pensare), essere raggiunto da:

text[2] = 'o';

Ora diamo un'occhiata a cosa fa ogni modo di dichiarare la stringa e come text[2] = 'o';quell'affermazione influenzerebbe le cose.

In primo luogo il modo più comunemente visto: char *text = "This is some text";. Cosa significa letteralmente? Bene, in C, significa letteralmente "Creare una variabile chiamata textche è un puntatore di lettura-scrittura a questa stringa letterale che si trova nello spazio di sola lettura (codice)". Se l'opzione è -Wwrite-stringsattivata, viene visualizzato un avviso come mostrato nella domanda sopra.

Fondamentalmente ciò significa "Avviso: hai tentato di creare una variabile che è di lettura-scrittura in un punto in cui non puoi scrivere". Se provi e poi imposta il terzo carattere su "o", in realtà stai cercando di scrivere in un'area di sola lettura e le cose non saranno belle. Su un PC tradizionale con Linux che risulta in:

Errore di segmentazione

Ora la seconda: char text[] = "This is some text";. Letteralmente, in C, ciò significa "Creare un array di tipo" char "e inizializzarlo con i dati" Questo è un po 'di testo \ 0 ". Le dimensioni dell'array saranno abbastanza grandi da memorizzare i dati". In modo che alloca effettivamente la RAM e copia il valore "This is some text \ 0" al momento dell'esecuzione. Nessun avviso, nessun errore, perfettamente valido. E il modo giusto per farlo se vuoi essere in grado di modificare i dati . Proviamo a eseguire il comando text[2] = 'o':

Questo è un po 'di testo

Ha funzionato perfettamente. Buona.

Ora la terza via: const char *text = "This is some text";. Ancora una volta il significato letterale: "Creare una variabile chiamata" testo "che è un puntatore di sola lettura a questi dati nella memoria di sola lettura.". Si noti che sia il puntatore che i dati sono ora di sola lettura. Nessun errore, nessun avviso. Cosa succede se proviamo ad eseguire il nostro comando test? Bene, non possiamo. Il compilatore è ora intelligente e sa che stiamo cercando di fare qualcosa di brutto:

errore: assegnazione della posizione di sola lettura '* (testo + 2u)'

Non si compila nemmeno. Cercare di scrivere nella memoria di sola lettura è ora protetto perché abbiamo detto al compilatore che il nostro puntatore è di memoria di sola lettura. Ovviamente, non deve puntare alla memoria di sola lettura, ma se la punti alla memoria di lettura-scrittura (RAM), quella memoria sarà comunque protetta dalla scrittura sul compilatore.

Infine, l'ultima forma: const char text[] = "This is some text";. Ancora una volta, come prima [], alloca un array nella RAM e copia i dati al suo interno. Tuttavia, ora questo è un array di sola lettura. Non puoi scrivergli perché il puntatore è taggato come const. Il tentativo di scriverlo comporta:

errore: assegnazione della posizione di sola lettura '* (testo + 2u)'

Quindi, un breve riassunto di dove siamo:

Questo modulo è completamente non valido e deve essere evitato a tutti i costi. Apre la porta a tutte le cose brutte che accadono:

char *text = "This is some text";

Questo modulo è il modulo giusto se si desidera rendere modificabili i dati:

char text[] = "This is some text";

Questo modulo è il modulo giusto se vuoi stringhe che non saranno modificate:

const char *text = "This is some text";

Questa forma sembra sprecare di RAM ma ha i suoi usi. Meglio dimenticarlo per ora però.

const char text[] = "This is some text";

6
Vale la pena notare che sugli Arduinos (almeno quelli basati su AVR), i letterali di stringa vivono nella RAM, a meno che non li dichiariate con una macro come PROGMEM, PSTR()o F(). Pertanto, const char text[]non utilizza più RAM di const char *text.
Edgar Bonet,

Teensyduino e molti altri più recenti compatibili con arduino inseriscono automaticamente valori letterali di stringa nello spazio del codice, quindi vale la pena controllare se sulla scheda è necessario o meno F ().
Craig

@ Craig.Feied In generale F () dovrebbe essere usato indipendentemente. Quelli che non "hanno bisogno" tendono a definirlo come un semplice (const char *)(...)casting. Nessun effetto reale se la scheda non ne ha bisogno, ma un grande risparmio se si trasferisce il codice su una scheda che lo fa.
Majenko

5

Per elaborare l'eccellente risposta di Makenko, c'è una buona ragione per cui il compilatore ti avverte di questo. Facciamo uno schizzo di prova:

char *foo = "This is some text";
char *bar = "This is some text";

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo [2] = 'o';     // change foo only
  Serial.println (foo);
  Serial.println (bar);
  }  // end of setup

void loop ()
  {
  }  // end of loop

Abbiamo due variabili qui, foo e bar. Modifico uno di quelli in setup (), ma vedo quale è il risultato:

Thos is some text
Thos is some text

Entrambi sono stati cambiati!

Infatti se guardiamo agli avvisi vediamo:

sketch_jul14b.ino:1: warning: deprecated conversion from string constant to char*’
sketch_jul14b.ino:2: warning: deprecated conversion from string constant to char*’

Il compilatore sa che questo è complicato ed è giusto! La ragione di ciò è che il compilatore (ragionevolmente) si aspetta che le costanti di stringa non cambino (dato che sono costanti). Pertanto, se si fa riferimento alla costante di stringa "This is some text"più volte nel codice, è consentito allocare la stessa memoria a tutte. Ora se ne modifichi uno, li modifichi tutti!


Fumo Santo! Chi avrebbe mai saputo ... È ancora vero per gli ultimi compilatori di ArduinoIDE? L'ho appena provato su un ESP32 e causa ripetuti errori GuruMeditation .
not2qubit

@ not2qubit Ho appena provato su Arduino 1.8.9 ed è vero lì.
Nick Gammon

Gli avvisi sono lì per un motivo. Questa volta ho ricevuto: avviso: ISO C ++ proibisce la conversione di una costante di stringa in 'char ' [-Wwrite-strings] char bar = "Questo è un po 'di testo"; - FORBIDS è una parola forte. Dato che ti è proibito farlo, il compilatore è libero di muck e condividere la stessa stringa su due variabili. Non fare cose proibite ! (Inoltre, leggi ed elimina gli avvisi). :)
Nick Gammon

Quindi, nel caso in cui ti imbatti in codice schifoso come questo, e vuoi sopravvivere alla giornata. Una dichiarazione iniziale *fooe l' *barutilizzo di diverse "costanti" di stringa impedirebbe che ciò accada? Inoltre, come è diverso dal non mettere affatto stringhe, come char *foo;:?
not2qubit

1
Costanti diverse potrebbero essere d'aiuto, ma personalmente non metterei nulla lì, e in seguito inserirò i dati nel solito modo (ad es. Con new, strcpye delete).
Nick Gammon

4

Smetti di provare a passare una costante di stringa in cui una funzione accetta a char*, oppure modifica la funzione in modo che prenda const char*invece una .

Le stringhe come "stringhe casuali" sono costanti.


Un testo come "caratteri casuali" è un carattere costante?
Federico Corazza,

1
I letterali stringa sono costanti stringa.
Ignacio Vazquez-Abrams,

3

Esempio:

void foo (char * s)
  {
  Serial.println (s);
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  foo ("bar");
  }  // end of setup

void loop ()
  {
  }  // end of loop

Avvertimento:

sketch_jul14b.ino: In function ‘void setup()’:
sketch_jul14b.ino:10: warning: deprecated conversion from string constant to ‘char*’

La funzione fooprevede un carattere * (che può quindi modificare) ma si sta passando una stringa letterale, che non deve essere modificata.

Il compilatore ti avverte di non farlo. Essendo deprecato, potrebbe trasformarsi da un avvertimento in un errore in una futura versione del compilatore.


Soluzione: fai in modo che foo prenda un carattere const *:

void foo (const char * s)
  {
  Serial.println (s);
  }

Non capisco Vuoi dire che non può essere modificato?

Le versioni precedenti di C (e C ++) ti consentono di scrivere codice come il mio esempio sopra. È possibile creare una funzione (come foo) che stampa qualcosa che si passa ad essa, quindi passare una stringa letterale (ad es. foo ("Hi there!");)

Tuttavia, una funzione che assume char *come argomento è consentito modificare il proprio argomento (cioè modificare Hi there!in questo caso).

Potresti aver scritto, ad esempio:

void foo (char * s)
  {
  Serial.println (s);
  strcpy (s, "Goodbye");
  }

Sfortunatamente, tramandando un letterale, ora hai potenzialmente modificato quel letterale in modo che "Ciao a tutti!" è ora "Arrivederci" che non va bene. In effetti, se hai copiato in una stringa più lunga, potresti sovrascrivere altre variabili. Oppure, su alcune implementazioni potresti ricevere una violazione di accesso perché "Ciao a tutti!" potrebbe essere stato inserito nella RAM di sola lettura (protetta).

Quindi gli autori di compilatori stanno gradualmente deprecando questo uso, in modo che le funzioni a cui si trasmette un valore letterale, debbano dichiarare tale argomento come const.


È un problema se non utilizzo un puntatore?
Federico Corazza,

Che tipo di problema? Quel particolare avvertimento riguarda la conversione di una costante di stringa in un puntatore char *. Puoi elaborare?
Nick Gammon

@Nick: cosa intendi con "(..) stai passando una stringa letterale, che non dovrebbe essere modificata". Non capisco Vuoi dire can notessere modificato?
Mads Skjern

Ho modificato la mia risposta. Majenko ha coperto la maggior parte di questi punti nella sua risposta.
Nick Gammon

1

Ho questo errore di compilazione:

TimeSerial.ino:68:29: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
   if(Serial.find(TIME_HEADER)) {

                         ^

Si prega di sostituire questa riga:
#define TIME_HEADER "T" // Header tag for serial time sync message

con questa linea:
#define TIME_HEADER 'T' // Header tag for serial time sync message

e la compilation va bene.


3
Questa modifica cambia la definizione da una stringa di un carattere "T" in un singolo carattere con il valore del codice ASCII per la lettera maiuscola T.
dlu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.