Come sbarazzarsi degli avvisi `conversione obsoleta da costante di stringa a 'char *'` in GCC?


409

Quindi sto lavorando su una base di codice estremamente grande e recentemente aggiornato a gcc 4.3, che ora attiva questo avviso:

avviso: conversione obsoleta dalla costante di stringa in 'char *'

Ovviamente, il modo corretto per risolvere questo problema è trovare ogni dichiarazione simile

char *s = "constant string";

o chiamata di funzione come:

void foo(char *s);
foo("constant string");

e renderli const charpuntatori. Tuttavia, ciò significherebbe toccare almeno 564 file, il che non è un compito che desidero svolgere in questo momento. Il problema in questo momento è che sto correndo -werror, quindi ho bisogno di un modo per soffocare questi avvisi. Come lo posso fare?


Quando vieni ad affrontare la sostituzione di 554 linee, sed è un buon amico. Assicurati di eseguire prima il backup.
Matt,

2
Ho esaminato le discussioni su come eliminare i messaggi di errore e quali dovrebbero essere le sostituzioni corrette. Non ho opinioni in merito. Tuttavia, penso che Matt sia sulla buona strada. Definisci cosa vuoi sostituire con cosa. Hai solo bisogno delle giuste espressioni regolari. Apporta le modifiche in una copia. Usa "diff" per confrontarli con l'originale. Apportare le modifiche usando sed è veloce, facile e gratuito, e diff è anche veloce, facile e gratuito. Provalo e vedi quante modifiche devi rivedere. Pubblica ciò che vuoi sostituire con ciò e lascia che gli utenti suggeriscano sostituzioni regex.
Thomas Hedden,

L'intera discussione manca del motivo per cui questo è un problema che deve essere risolto in base all'avvertimento gcc. Il motivo è nella risposta di David Schwartz stackoverflow.com/questions/56522654/… .
Andig

Risposte:


227

Credo che passare -Wno-write-stringsa gcc eliminerà questo avviso.


6
Può essere disabilitato su per file base usando pragmas.
Priyank Bolia,

18
@PriyankBolia bdonlan ha commentato la risposta di Rob Walker che può usare #pragma GCC diagnostic ignored "-Wwrite-strings".
MasterMastic,

9
Tranne se si controlla l'API, nel qual caso la risposta di John di seguito, sulla modifica della firma per accettare const char *, è più corretta.
jcwenger,

215
QUESTA È UNA PRATICA ORRIBAMENTE MALE, e sono triste che abbia ottenuto tutti quei voti. Gli avvisi non sono presenti in modo da ignorarli. Ci sono avvertimenti che ti dicono "amico, stai facendo qualcosa che potrebbe essere sbagliato, stai attento", e dovresti sopprimerli solo quando vuoi rispondere come "stai zitto, so cosa sto facendo", il che è molto probabile non è il caso dei programmatori neonati.
Il fisico quantico

9
Sono d'accordo, non dovresti liberarti dell'avvertimento e invece utilizzare la soluzione fornita da John. Peccato che questa sia la risposta accettata!
Jérôme,

564

Qualsiasi funzione in cui passi letterali stringa "I am a string literal"dovrebbe usare char const *come tipo anziché char*.

Se hai intenzione di riparare qualcosa, correggilo nel modo giusto.

Spiegazione:

Non è possibile utilizzare valori letterali di stringa per inizializzare le stringhe che verranno modificate, poiché sono di tipo const char*. Casting via la costanza per modificarle in seguito è un comportamento indefinito , quindi bisogna copiare i const char*fili charda charin allocati dinamicamente char*stringhe al fine di modificarli.

Esempio:

#include <iostream>

void print(char* ch);

void print(const char* ch) {
    std::cout<<ch;
}

int main() {
    print("Hello");
    return 0;
}

25
Anche se questo è vero, non hai sempre il controllo sulle API di terze parti che potrebbero non utilizzare correttamente char */ const char *, quindi in quel caso normalmente lancio.
ideasman42

15
@ppumkin Sfortunatamente, molte funzioni di stringa della libreria C standard accettano argomenti come char*anche per le stringhe che non verranno modificate. Se prendi un parametro come a char const*e lo passi a una funzione standard che accetta un char*, lo colpirai. Se la funzione di libreria non manipolerà la stringa, è possibile eliminare const.
John,

Solo perché non è sempre possibile non significa che non sia l'opzione preferita per molte volte in cui questo avviso appare nel codice di produzione comune.
Ama

1
Ora comprendo appieno la soluzione e la funzionalità dei letterali di stringa. Ma forse altri no, quindi 'ritengo' la necessità di una spiegazione
NicoBerrogorry il

1
Non capisco come applicare la soluzione :(
desmond13

69

Ho avuto un problema simile, l'ho risolto in questo modo:

#include <string.h>

extern void foo(char* m);

int main() {
    // warning: deprecated conversion from string constant to ‘char*’
    //foo("Hello");

    // no more warning
    char msg[] = "Hello";
    foo(msg);
}

È un modo appropriato di risolverlo? Non ho accesso ad fooadattarlo per accettarlo const char*, anche se sarebbe una soluzione migliore (perché foonon cambia m).


8
@elcuco, cosa proporresti? Non ho potuto modificare pippo e ho cercato di trovare una soluzione che non richiedesse la soppressione dell'avvertimento. Nel mio caso quest'ultimo era più una questione di esercizio fisico, ma per il poster originale sembrava importante. Per quanto ne so, la mia risposta è l'unica in grado di risolvere contemporaneamente le condizioni mia e del PO, in modo che possa essere una risposta preziosa a qualcuno. Se ritieni che la mia soluzione non sia abbastanza valida, potresti fornire un'alternativa? (Ciò non include la modifica di foo o l'
ignoramento

se supponiamo che foo sia correttamente codificato (cosa che sfortunatamente non sembra essere il caso del codice "Josh Matthews" di cui parla) questa è la soluzione migliore. questo perché se la funzione deve effettivamente cambiare la stringa 'msg' passandola una stringa costante spezzerebbe il codice, giusto? ma comunque questo non sembra rispondere alla domanda perché gli errori sono già nel vecchio codice e non nel nuovo, quindi avrebbe bisogno di cambiare comunque il vecchio codice.
João Portela,

Questo è anche l'approccio che ho seguito. E se qualcuno sta cercando questo per i casi di char **in PyArg_ParseTupleAndKeywordsfaccio qualcosa del genere:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
precipitosamente

@elcuco: non sono sicuro di come funzionano gli array statici C ++. Copieranno davvero dei dati e non solo un puntatore?
Alexander Malakhov,

Sebbene in alcuni casi questo approccio possa avere valore applicandolo alla cieca, è probabile che l'IMO faccia più danni che benefici. Applicare questo alla cieca potrebbe facilmente portare a indicazioni penzolanti. Inoltre gonferà il codice con copie di stringhe inutili.
washwash


30

Se è una base di codice attiva, potresti comunque voler aggiornare la base di codice. Ovviamente, eseguire le modifiche manualmente non è possibile, ma credo che questo problema possa essere risolto una volta per tutte con un singolo sedcomando. Non l'ho provato, comunque, quindi prendi quanto segue con un granello di sale.

find . -exec sed -E -i .backup -n \
    -e 's/char\s*\*\s*(\w+)\s*= "/char const* \1 = "/g' {} \;

Questo potrebbe non trovare tutti i posti (anche senza considerare le chiamate di funzione) ma alleverebbe il problema e consentirebbe di eseguire manualmente le poche modifiche rimanenti.


7
che risolve solo avvisi di dichiarazioni e non funziona chiamate +1 per sed fu comunque: p
João Portela,

25

Non riesco a usare l'interruttore del compilatore. Quindi ho girato questo:

char *setf = tigetstr("setf");

a questo:

char *setf = tigetstr((char *)"setf");

1
+1: non è possibile modificare il valore delle applicazioni, ma solo il valore. questo ha dimostrato di risolvere il vero problema. altri risolvono solo alcuni problemi con il compilatore.
elcuco,

1
La cosa veramente fastidiosa è che tigetstr () dovrebbe essere prototipato con un (const char *), non un (char *)
vy32

2
Quando lo faccio ricevo "avviso: cast dal tipo 'const char *' al tipo 'char *' lancia invece costanza". Ho dovuto usare un const_cast per sbarazzarmi di tutti gli avvertimenti: const_cast <char *> ("setf")
CrouZ

2
Penso che il cast di const sia la prima soluzione accettabile in questa pagina (tranne la modifica dell'API).
primo

25

Ecco come farlo in linea in un file, quindi non devi modificare il tuo Makefile.

// gets rid of annoying "deprecated conversion from string constant blah blah" warning
#pragma GCC diagnostic ignored "-Wwrite-strings"

È quindi possibile in seguito ...

#pragma GCC diagnostic pop

25

Sostituire

char *str = "hello";

con

char *str = (char*)"hello";

o se stai chiamando in funzione:

foo("hello");

sostituirlo con

foo((char*) "hello");

15

Invece di:

void foo(char *s);
foo("constant string");

Questo funziona:

void foo(const char s[]);
foo("constant string");

Questo è il modo corretto di farlo poiché non dovresti passare una stringa (costante) a una funzione che si aspetta comunque una stringa non costante!
jfla,

15

In C ++, utilizzare const_castcome di seguito

char* str = const_cast<char*>("Test string");

7

Test stringè una stringa const. Quindi puoi risolvere in questo modo:

char str[] = "Test string";

o:

const char* str = "Test string";
printf(str);

4

Perché non usare solo il tipo casting?

(char*) "test"

2

Esegui la tipografia da stringa costante a puntatore char, ad es

char *s = (char *) "constant string";

1

In C ++, sostituisci:

char *str = "hello";

con:

std::string str ("hello");

E se vuoi confrontarlo:

str.compare("HALLO");

1

Non capisco come applicare la tua soluzione :( - kalmanIsAGameChanger

Lavorando con Arduino Sketch, avevo una funzione che causava i miei avvertimenti.

Funzione originale: char StrContains (char * str, char * sfind)

Per interrompere gli avvertimenti ho aggiunto la const davanti al carattere * str e al carattere * sfind.

Modificato: char StrContains (const char * str, const char * sfind).

Tutti gli avvertimenti sono andati via.


Questa è la risposta corretta secondo l'avviso che diceva: "avviso: conversione obsoleta dalla costante di stringa a 'char *'".
Norbert Boros,

0

vedi questa situazione:

typedef struct tagPyTypeObject
{
    PyObject_HEAD;
    char *name;
    PrintFun print;
    AddFun add;
    HashFun hash;
} PyTypeObject;

PyTypeObject PyDict_Type=
{
    PyObject_HEAD_INIT(&PyType_Type),
    "dict",
    dict_print,
    0,
    0
};

guarda il campo del nome, in gcc si compila senza preavviso, ma in g ++ lo farà, non so perché.


gcc implica trattare il file come file sorgente C, g ++ lo tratta come file sorgente c ++, a meno che non venga sovrascritto da -x ?? opzione. Quindi un linguaggio diverso, c e c ++ ha sottili differenze su ciò che dovrebbe essere un avvertimento.
zhaorufei,

0

Puoi anche creare una stringa scrivibile da una costante di stringa chiamando strdup().

Ad esempio, questo codice genera un avviso:

putenv("DEBUG=1");

Tuttavia, il seguente codice non lo fa (crea una copia della stringa sull'heap prima di passarla a putenv):

putenv(strdup("DEBUG=1"));

In questo caso (e forse nella maggior parte degli altri), disattivare l'avviso è una cattiva idea: è lì per un motivo. L'altra alternativa (rendere tutte le stringhe scrivibili per impostazione predefinita) è potenzialmente inefficiente.

Ascolta cosa ti sta dicendo il compilatore!


6
E perde anche la memoria allocata per quella stringa scrivibile.
RBerteig,

1
Sì lo fa - questo è apposta. Non è un problema con il codice una tantum (ad es. Inizializzazione), come sopra. In alternativa, puoi gestire tu stesso la memoria e rilasciarla al termine.
BillAtHRST,

1
Il caso particolare di putenv()è irto - non è una buona scelta di esempio (almeno, non senza molta più discussione su cosa putenv()fa di quanto non ci sia in questa risposta). È una discussione completamente separata. (Si noti che la specifica POSIX per il comportamento di putenv()è problematica, in base alle implementazioni legacy di prima della definizione di POSIX.) IIRC, c'era un bug in una recente (questo millennio) versione della libreria GNU C correlata al putenv()cambio di comportamento, e di essere cambiato.)
Jonathan Leffler il

0

basta usare l'opzione -w per g ++

esempio:

g ++ -w -o simple.o simple.cpp -lpthread

Ricorda che questo non evita la deprecazione, ma impedisce di mostrare messaggi di avviso sul terminale.

Ora se vuoi davvero evitare la deprecazione usa la parola chiave const in questo modo:

const char* s="constant string";  

0

Perché non usi l' -Wno-deprecatedopzione per ignorare i messaggi di avviso obsoleti?


0

Il problema in questo momento è che sto correndo con -Werror

Questo è il tuo vero problema, IMO. Puoi provare alcuni modi automatizzati di passare da (char *) a (const char *) ma vorrei mettere soldi su di loro non solo lavorando. Dovrai coinvolgere un essere umano per almeno parte del lavoro. A breve termine, basta ignorare l'avvertimento (ma l'IMO lo lascia attivo o non verrà mai risolto) e rimuovere semplicemente -Werror.


9
La ragione per cui la gente usa -Werror è così che gli avvertimenti non vengono risolti. Altrimenti non vengono mai riparati.
Zan Lynx,

2
Il motivo per cui le persone usano -Werror è perché hanno lavorato solo su progetti di giocattoli o sono masochisti. La mancata compilazione del codice a causa di un aggiornamento GCC è un vero problema quando hai 100k + LOC. Dito. qualcuno che aggiunge spazzatura come "-Wno-write-strings" alla build per sbarazzarsi dei fastidiosi avvisi (come suggerisce il commento più votato in questo post).
James Antill,

2
c'è un chiaro disaccordo in questo argomento, ad esempio programmatore.97things.oreilly.com/wiki/index.php/…
João Portela,

3
@James: fai un punto interessante, ma deve esserci un modo migliore. Sembra inutile non correggere immediatamente gli avvisi: come si riconosce quando un nuovo codice ha invocato un nuovo avviso quando non sono stati rimossi tutti i vecchi avvisi? Nella mia esperienza, ciò porta solo le persone a ignorare gli avvertimenti che non dovrebbero ignorare.
nobar,

2
@James: il nostro progetto di giocattoli è 1.5 + M LOC (multilingue). Come ha detto nobar, -Werror evita di ignorare gli avvertimenti che non dovrebbero essere e sì, ogni volta che si alza una nuova versione del compilatore, dobbiamo ricontrollare tutto. -Wno-write-strings viene utilizzato solo quando si utilizza Boost per wrapper python in un modo file per file, perché non riscriveremo Boost (e ora, 2017, preferiamo non usare più Boost ma C ++ 11 / Cython). Ogni avviso ignorato deve quindi essere periodicamente rivisto da un controllo di qualità per vedere se ora possono essere evitati dal codice o se non è ancora possibile.
msn,

0

Grazie a tutti per l'aiuto. Scegliere da qui e là arriva questa soluzione. Questo compila pulito. Non ho ancora testato il codice. Forse domani...

const char * timeServer[] = { "pool.ntp.org" }; // 0 - Worldwide 
#define WHICH_NTP            0 // Which NTP server name to use.
...
sendNTPpacket(const_cast<char*>(timeServer[WHICH_NTP])); // send an NTP packet to a server
...
void sendNTPpacket(char* address) { code }

Lo so, c'è solo 1 oggetto nell'array timeServer. Ma potrebbe essercene di più. Il resto è stato commentato per ora per risparmiare memoria.


-1
PyTypeObject PyDict_Type=
{ ...

PyTypeObject PyDict_Type=
{
  PyObject_HEAD_INIT(&PyType_Type),
                     "dict",
                     dict_print,
                     0,
                     0
}; 

guarda il campo del nome, in gcc si compila senza preavviso, ma in g ++ lo farà, non so perché.

in gcc (Compiling C), -Wno-write-strings è attivo per impostazione predefinita.

in g++ (Compiling C++)-Wwrite-strings è attivo per impostazione predefinita

Questo è il motivo per cui esiste un comportamento diverso. Per noi l'utilizzo di macro di Boost_pythongenera tali avvisi. Quindi usiamo -Wno-write-stringsquando compiliamo C ++ poiché usiamo sempre-Werror


-1

Dichiarare la stringa come constrisolverà il problema:

char const*s = "constant string";
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.