Comment se débarrasser des avertissements `conversion obsolète de constante de chaîne en 'char *' 'dans GCC?

409

Je travaille donc sur une base de code extrêmement grande et récemment mise à niveau vers gcc 4.3, ce qui déclenche maintenant cet avertissement:

avertissement: conversion obsolète de la constante de chaîne en 'char *'

De toute évidence, la bonne façon de résoudre ce problème est de trouver chaque déclaration comme

char *s = "constant string";

ou appel de fonction comme:

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

et en faire des const charpointeurs. Cependant, cela signifierait toucher 564 fichiers, au minimum, ce qui n'est pas une tâche que je souhaite effectuer à ce stade. Le problème en ce moment est que je cours avec -werror, j'ai donc besoin d'un moyen d'étouffer ces avertissements. Comment puis je faire ça?

Josh Matthews
la source
Lorsque vous tentez de remplacer 554 lignes, sed est un bon ami. Assurez-vous cependant de sauvegarder d'abord.
Matt
2
J'ai regardé les discussions sur la façon de supprimer les messages d'erreur et quels devraient être les remplacements corrects. Je n'ai pas d'opinion là-dessus. Cependant, je pense que Matt est sur la bonne voie. Définissez ce que vous voulez remplacer par quoi. Vous avez juste besoin de la ou des expressions régulières appropriées. Apportez les modifications dans une copie. Utilisez "diff" pour les comparer avec l'original. Les modifications à l'aide de sed sont rapides, faciles et gratuites, et diff est également rapide, facile et gratuit. Essayez-le et voyez combien de changements vous devez revoir. Publiez ce que vous voulez remplacer par quoi et laissez les utilisateurs suggérer des remplacements regex.
Thomas Hedden
Toute la discussion ne comprend pas pourquoi il s'agit d'un problème qui doit être résolu selon l'avertissement gcc. La raison en est dans la réponse de David Schwartz stackoverflow.com/questions/56522654/… .
andig

Réponses:

227

Je crois que passer -Wno-write-stringsà gcc supprimera cet avertissement.

DGentry
la source
6
Est-il possible de le désactiver par fichier de base à l'aide de pragmas.
Priyank Bolia
18
@PriyankBolia bdonlan a commenté la réponse de Rob Walker qu'il peut utiliser #pragma GCC diagnostic ignored "-Wwrite-strings".
MasterMastic
9
Sauf si vous contrôlez l'API, auquel cas la réponse de @ John ci-dessous, à propos de la modification de la signature pour accepter const char *, est plus correcte.
jcwenger
215
C'EST UNE HORRIBLEMENT MAUVAISE PRATIQUE, et je suis triste d'avoir obtenu tous ces votes. Les avertissements ne sont pas là pour que vous les ignoriez. Des avertissements vous disent "mec, tu fais quelque chose qui pourrait être mal, sois prudent", et tu ne devrais les supprimer que lorsque tu veux répondre comme "tais-toi, je sais ce que je fais", ce qui est très probablement pas le cas avec les programmeurs pour nourrissons.
The Quantum Physicist
9
Je suis d'accord, vous ne devriez pas vous débarrasser de l'avertissement et utiliser à la place la solution fournie par John. Dommage que celui-ci soit la réponse acceptée!
Jérôme
564

Toutes les fonctions dans lesquelles vous passez des littéraux de chaîne "I am a string literal"doivent utiliser char const *comme type au lieu de char*.

Si vous voulez réparer quelque chose, corrigez-le correctement.

Explication:

Vous ne pouvez pas utiliser de littéraux de chaîne pour initialiser des chaînes qui seront modifiées, car elles sont de type const char*. A bas les constness pour modifier plus tard eux est un comportement non défini , vous devez copier vos const char*chaînes charpar charen alloués dynamiquement des char*chaînes afin de les modifier.

Exemple:

#include <iostream>

void print(char* ch);

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

int main() {
    print("Hello");
    return 0;
}
John
la source
25
Bien que cela soit vrai, vous n'avez pas toujours le contrôle sur les API tierces qui pourraient ne pas utiliser correctement char */ const char *, donc dans ce cas, je casse normalement.
ideasman42
15
@ppumkin Malheureusement, de nombreuses fonctions de chaîne de bibliothèque standard C prennent des arguments comme char*même pour les chaînes qui ne seront pas modifiées. Si vous prenez un paramètre en tant que char const*et le passez à une fonction standard en prenant un, char*vous l'atteindrez. Si la fonction de bibliothèque ne manipule pas la chaîne, vous pouvez supprimer le const.
John
Ce n'est pas toujours possible, cela ne signifie pas que ce n'est pas l'option préférée dans la plupart des cas, cet avertissement apparaît dans le code de production commun.
LovesTha
1
Je comprends maintenant parfaitement la solution et la fonctionnalité des littéraux de chaîne. Mais peut-être que d'autres ne le font pas, donc je «tiens» le besoin d'une explication
NicoBerrogorry
1
Je ne comprends pas comment appliquer votre solution :(
desmond13
69

J'ai eu un problème similaire, je l'ai résolu comme ceci:

#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);
}

Est-ce une façon appropriée de résoudre ce problème? Je n'ai pas accès àfoo adapter pour l'accepter const char*, même si ce serait une meilleure solution (car fooça ne change pas m).

BlackShift
la source
8
@elcuco, que proposeriez-vous? Je n'ai pas pu modifier foo et j'ai essayé de trouver une solution qui ne nécessitait pas la suppression de l'avertissement. Dans mon cas, ce dernier était plus une question d'exercice, mais pour l'affiche originale, cela semblait important. Pour autant que je sache, ma réponse est la seule qui permettrait de résoudre à la fois mes conditions et celles de l'OP, ce qui pourrait être une réponse précieuse pour quelqu'un. Si vous pensez que ma solution n'est pas assez bonne, pourriez-vous s'il vous plaît fournir une alternative? (Cela n'inclut pas l'édition foo ou l'ignorance de l'avertissement.)
BlackShift
si nous supposons que foo est correctement codé (ce qui ne semble malheureusement pas être le cas pour le code dont parle «Josh Matthews»), c'est la meilleure solution. c'est parce que si la fonction doit réellement changer la chaîne 'msg' en la passant, une chaîne constante casserait le code, non? mais quoi qu'il en soit, cela ne semble pas répondre à la question car les erreurs sont déjà dans l'ancien code et non dans le nouveau, il devrait donc changer l'ancien code de toute façon.
João Portela
C'est l'approche que j'ai également adoptée. Et si quelqu'un est à la recherche ce pour les cas de char **dans PyArg_ParseTupleAndKeywordsje fais quelque chose comme ceci:static char kw[][16] = {"mode", "name", "ip", "port"}; static char * kwlist[] = {kw[0], kw[1], kw[2], kw[3], NULL};
dashesy
@elcuco: Je ne sais pas comment fonctionnent les tableaux statiques C ++. Est-ce que cela copiera vraiment des données, et pas seulement un pointeur?
Alexander Malakhov
Bien que cette approche puisse avoir du mérite dans certains cas, son application aveugle est susceptible de faire plus de mal que de bien. L'application aveugle de ces informations pourrait facilement conduire à des balancements. Il gonflera également le code avec des copies de chaînes inutiles.
plugwash
30

S'il s'agit d'une base de code active, vous souhaiterez peut-être toujours mettre à niveau la base de code. Bien sûr, effectuer les modifications manuellement n'est pas possible mais je pense que ce problème pourrait être résolu une fois pour toutes par une seule sedcommande. Je ne l'ai pas encore essayé, alors prenez ce qui suit avec un grain de sel.

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

Cela pourrait ne pas trouver tous les emplacements (même en ne tenant pas compte des appels de fonction), mais cela atténuerait le problème et permettrait d'effectuer les quelques modifications restantes manuellement.

Konrad Rudolph
la source
7
cela ne résout que les avertissements de déclarations et non les appels de fonction +1 pour sed fu de toute façon: p
João Portela
25

Je ne peux pas utiliser le commutateur du compilateur. J'ai donc tourné ceci:

char *setf = tigetstr("setf");

pour ça:

char *setf = tigetstr((char *)"setf");
vy32
la source
1
+1 - vous ne pouvez pas changer la valeur des applications, seulement la valeur r. cela s'est avéré résoudre le vrai problème. d'autres contournent juste quelques problèmes avec le compilateur.
elcuco
1
Ce qui est vraiment ennuyeux, c'est que tigetstr () devrait être prototypé avec un (const char *), pas un (char *)
vy32
2
Lorsque je fais cela, j'obtiens "avertissement: transtypez de type 'const char *' en type 'char *' rejette constness" à la place. J'ai dû utiliser un const_cast pour me débarrasser de tous les avertissements: const_cast <char *> ("setf")
CrouZ
2
Je pense que la fonte const est la première solution acceptable sur cette page (sauf le changement d'API).
rwst
25

Voici comment le faire en ligne dans un fichier, vous n'avez donc pas à modifier votre Makefile.

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

Vous pourrez alors plus tard ...

#pragma GCC diagnostic pop
EdH
la source
25

Remplacer

char *str = "hello";

avec

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

ou si vous appelez en fonction:

foo("hello");

remplacer cela par

foo((char*) "hello");
takataka
la source
15

Au lieu de:

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

Cela marche:

void foo(const char s[]);
foo("constant string");
John
la source
C'est la bonne façon de le faire car vous ne devez pas passer une chaîne (constante) à une fonction qui attend de toute façon une chaîne non constante!
jfla
15

En C ++, utilisez le const_castcomme ci-dessous

char* str = const_cast<char*>("Test string");
appapurapu
la source
7

Test stringest une chaîne de const. Vous pouvez donc résoudre comme ceci:

char str[] = "Test string";

ou:

const char* str = "Test string";
printf(str);
alexsid
la source
4

Pourquoi ne pas simplement utiliser la conversion de type?

(char*) "test"
Dario
la source
2

Faire un transtypage de type chaîne constante vers un pointeur char

char *s = (char *) "constant string";
tejp124
la source
1

En C ++, remplacez:

char *str = "hello";

avec:

std::string str ("hello");

Et si vous voulez le comparer:

str.compare("HALLO");
Sohrab
la source
1

Je ne comprends pas comment appliquer votre solution :( - kalmanIsAGameChanger

En travaillant avec Arduino Sketch, j'avais une fonction à l'origine de mes avertissements.

Fonction d'origine: char StrContains (char * str, char * sfind)

Pour arrêter les avertissements, j'ai ajouté le const devant le char * str et le char * sfind.

Modifié: char StrContains (const char * str, const char * sfind).

Tous les avertissements ont disparu.

MyGEARStationcom
la source
Ceci est la bonne réponse selon l'avertissement: "avertissement: conversion obsolète de la constante de chaîne en 'char *'".
Norbert Boros
0

voir cette situation:

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
};

regardez le champ du nom, dans gcc il compile sans avertissement, mais dans g ++ il le fera, je ne sais pas pourquoi.

une fenêtre
la source
gcc implique de traiter le fichier comme un fichier source C, g ++ le traite comme un fichier source c ++, à moins qu'il ne soit remplacé par -x ?? option. Donc, un langage différent, c et c ++ a de subtiles différences sur ce qui devrait être un avertissement.
zhaorufei
0

Vous pouvez également créer une chaîne inscriptible à partir d'une constante de chaîne en appelant strdup() .

Par exemple, ce code génère un avertissement:

putenv("DEBUG=1");

Cependant, le code suivant ne fonctionne pas (il fait une copie de la chaîne sur le tas avant de la transmettre putenv):

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

Dans ce cas (et peut-être dans la plupart des autres), désactiver l'avertissement est une mauvaise idée - c'est là pour une raison. L'autre alternative (rendre toutes les chaînes accessibles en écriture par défaut) est potentiellement inefficace.

Écoutez ce que le compilateur vous dit!

BillAtHRST
la source
6
Et il fuit également la mémoire allouée à cette chaîne inscriptible.
RBerteig
1
Oui, c'est exprès. Pas un problème avec le code à usage unique (par exemple, l'initialisation), comme ci-dessus. Ou, vous pouvez gérer la mémoire vous-même et la libérer lorsque vous en avez terminé.
BillAtHRST
1
Le cas particulier de putenv()est lourd - ce n'est pas un bon choix d'exemple (du moins, pas sans beaucoup plus de discussions sur ce qui putenv()fait qu'il n'y en a dans cette réponse). C'est une discussion complètement séparée. (Notez que la spécification POSIX pour le comportement de putenv()est problématique, basée sur les implémentations héritées d'avant la définition de POSIX.) IIRC, il y avait un bogue dans une version récente (ce millénaire) de la bibliothèque GNU C qui était liée au putenv()changement de comportement, et être changé en arrière.)
Jonathan Leffler
0

utilisez simplement l'option -w pour g ++

exemple:

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

Rappelez-vous que cela n'évite pas la dépréciation, mais empêche d'afficher un message d'avertissement sur le terminal.

Maintenant, si vous voulez vraiment éviter la dépréciation, utilisez le mot-clé const comme ceci:

const char* s="constant string";  
Md. Arafat Al Mahmud
la source
0

Pourquoi n'utilisez-vous pas l' -Wno-deprecatedoption pour ignorer les messages d'avertissement obsolètes?

Drew Noakes
la source
0

Le problème en ce moment est que je cours avec -Werror

C'est votre vrai problème, OMI. Vous pouvez essayer des moyens automatisés de passer de (char *) à (const char *) mais je mettrais de l'argent dessus non seulement en travaillant. Vous devrez impliquer un humain pour au moins une partie du travail. Pour le court terme, ignorez simplement l'avertissement (mais IMO laissez-le allumé, sinon il ne sera jamais corrigé) et supprimez simplement -Werror.

James Antill
la source
9
Si les gens utilisent -Werror est donc que les avertissements ne se fixe. Sinon, ils ne sont jamais réparés.
Zan Lynx
2
La raison pour laquelle les gens utilisent -Werror est parce qu'ils n'ont travaillé que sur des projets de jouets ou parce qu'ils sont masochistes. Avoir votre code ne parvient pas à se construire en raison d'une mise à jour GCC est un vrai problème lorsque vous avez 100k + LOC. Dito. quelqu'un qui ajoute des fichiers indésirables comme "-Wno-write-strings" à la construction pour se débarrasser des avertissements ennuyeux (comme le suggère le commentaire le mieux noté dans ce post).
James Antill
2
il y a clairement désaccord sur ce sujet, par exemple programmer.97things.oreilly.com/wiki/index.php/…
João Portela
3
@James: Vous soulevez un point intéressant, mais il doit y avoir un meilleur moyen. Il semble inutile de ne pas corriger les avertissements immédiatement - comment reconnaître quand un nouveau code a invoqué un nouvel avertissement alors que vous n'avez pas supprimé tous les anciens avertissements? D'après mon expérience, cela conduit simplement les gens à ignorer les avertissements qu'ils ne devraient pas ignorer.
nobar
2
@James: notre projet de jouet est 1.5 + M LOC (multi-langue). Comme l'a dit nobar, -Werror évite d'ignorer les avertissements qui ne devraient pas l'être et oui, chaque fois qu'une nouvelle version du compilateur se lève, nous devons tout revérifier. -Wno-write-strings est juste utilisé lors de l'utilisation de Boost pour les wrappers python dans un fichier par fichier, car nous n'allons pas réécrire Boost (et pour l'instant, 2017, nous préférons ne plus utiliser Boost mais C ++ 11 / cython). Chaque avertissement ignoré doit ensuite être revu périodiquement par contrôle de qualité pour voir s'il peut désormais être évité par code ou s'il n'est pas encore possible.
msn
0

Merci à tous pour l'aide. Choisir ici et là vient cette solution. Cela compile propre. Je n'ai pas encore testé le code. Demain peut-être...

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 }

Je sais, il n'y a qu'un seul élément dans le tableau timeServer. Mais il pourrait y en avoir plus. Le reste a été commenté pour l'instant pour économiser de la mémoire.

Micheal Morrow
la source
-1
PyTypeObject PyDict_Type=
{ ...

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

regardez le champ du nom, dans gcc il compile sans avertissement, mais dans g ++ il le fera, je ne sais pas pourquoi.

dans gcc (Compiling C) , -Wno-write-strings est actif par défaut.

dans g++ (Compiling C++) -Wwrite-strings est actif par défaut

C'est pourquoi il y a un comportement différent. Pour nous, l'utilisation de macros de Boost_pythongénère de tels avertissements. Nous utilisons donc -Wno-write-stringslors de la compilation C ++ puisque nous utilisons toujours-Werror

msn
la source
-1

Déclarez une chaîne comme constrésoudra le problème:

char const*s = "constant string";
Jaa Hsn
la source