Conversion obsolète C ++ de la constante de chaîne en 'char *'

154

J'ai une classe avec un private char str[256];

et pour cela j'ai un constructeur explicite:

explicit myClass(const char *func)
{
    strcpy(str,func);
}

Je l'appelle comme:

myClass obj("example");

Lorsque je compile ceci, je reçois l'avertissement suivant:

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

Pourquoi cela arrive-t-il?

mkamthan
la source
1
Vous devriez utiliser strncpy(str, func, 255)au lieu de strcpy(str, func)pour une copie plus sûre. Et puis n'oubliez pas d'ajouter le '\ 0' à la fin de la chaîne car strncpy ne l'ajoute pas.
Patrice Bernassola
2
Il est encore plus sûr de dire "strncpy (str, func, sizeof (str)); str [sizeof (str) - 1] = '\ 0';"
Warren Young
3
Je ne pense pas que ce qui précède donne l'avertissement que vous avez cité, même si je suis sûr qu'un code assez similaire le ferait. Afin d'obtenir des réponses significatives, vous devez publier un exemple de compilation minimal qui produit l'avertissement.
sbi
3
@Patrice, Warren: n'utilisez pas strncpy, ce n'est pas une version plus sûre de strcpy. Utilisez (ou réinstallez) strcpy_s.
Steve Jessop
J'ai eu le problème, il ne montre ces problèmes que pour une version -X86 et non pour les versions Solaris ou ARM (cible) normales, donc j'ignore cela. Impossible de trouver un correctif, car il n'affiche pas d'avertissement normalement pour mon exemple de code. Merci à tous!
mkamthan

Réponses:

144

Il s'agit d'un message d'erreur que vous voyez chaque fois que vous rencontrez une situation comme celle-ci:

char* pointer_to_nonconst = "string literal";

Pourquoi? Eh bien, C et C ++ diffèrent par le type de littéral de chaîne. En C, le type est un tableau de char et en C ++, c'est un tableau constant de char. Dans tous les cas, vous n'êtes pas autorisé à modifier les caractères du littéral de chaîne, donc le const en C ++ n'est pas vraiment une restriction mais plutôt une sécurité de type. Une conversion de const char*à char*n'est généralement pas possible sans un cast explicite pour des raisons de sécurité. Mais pour la compatibilité descendante avec C, le langage C ++ permet toujours d'attribuer un littéral de chaîne à a char*et vous avertit que cette conversion est obsolète.

Donc, quelque part, il vous manque un ou plusieurs consts dans votre programme pour la correction de const. Mais le code que vous nous avez montré n'est pas le problème car il n'effectue pas ce type de conversion obsolète. L'avertissement doit provenir d'un autre endroit.

sellibitze
la source
17
Compte tenu du point de vue et des votes sur cette question, il est regrettable que le PO n'ait jamais fourni de code qui démontre réellement le problème.
Shafik Yaghmour
1
Vous pouvez reproduire le problème avec le code de l'OP en supprimant le constdu MyClassconstructeur ... puis vous pouvez le résoudre en ajoutant le constback.
Theodore Murdock
145

L'avertissement:

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

est donné parce que vous faites quelque chose comme:

void foo(char* str);
foo("hello");

Le problème est que vous essayez de convertir une chaîne littérale (avec type const char[]) en char*.

Vous pouvez convertir un const char[]en const char*parce que le tableau se désintègre en pointeur, mais ce que vous faites est de faire d'une mutable une constante.

Cette conversion est probablement autorisée pour la compatibilité C et vous donne juste l'avertissement mentionné.

fnieto - Fernando Nieto
la source
96

Comme réponse non. 2 par fnieto - Fernando Nieto décrit clairement et correctement que cet avertissement est donné parce que quelque part dans votre code vous faites (pas dans le code que vous avez posté) quelque chose comme:

void foo(char* str);
foo("hello");

Cependant, si vous souhaitez également garder votre code sans avertissement, effectuez simplement les modifications correspondantes dans votre code:

void foo(char* str);
foo((char *)"hello");

Autrement dit, lancez simplement la stringconstante sur (char *).

sactiw
la source
17
Sinon, faites la fonction: void foo (const char * str)
Caprooja
3
@Caprooja Oui déclarer le paramètre comme 'pointeur vers une constante' fonctionnera également dans ce cas. Mais avec ce changement, l'utilisateur ne peut plus changer / réaffecter la valeur stockée à l'adresse en utilisant le pointeur 'str' que l'utilisateur pourrait faire dans la partie implémentation. C'est donc quelque chose que vous voudrez peut-être rechercher.
sactiw
1
@sactiw Y a-t-il des raisons de rester void foo(char* str)tel quel? Je pensais que nous ne pouvions pas modifier strde footoute façon, même le paramètre est écrit comme non-const.
kgf3JfUtW
37

Il existe 3 solutions:

Solution 1:

const char *x = "foo bar";

Solution 2:

char *x = (char *)"foo bar";

Solution 3:

char* x = (char*) malloc(strlen("foo bar")+1); // +1 for the terminator
strcpy(x,"foo bar");

Les tableaux peuvent également être utilisés à la place des pointeurs car un tableau est déjà un pointeur constant.

anilbey
la source
7
Pour la solution 3, il y en a strdup. Contrairement à votre code, il allouera de l'espace pour le caractère NUL de fin et ne dépassera pas l'allocation.
Ben Voigt
2
La solution 2 est à éviter.
Courses de légèreté en orbite
En fait, la solution 2 peut être: char * x = static_cast <char *> ("foo bar") en C ++.
Kehe CAI
3
Allez-vous intégrer les commentaires dans votre réponse? La solution 3 est toujours dangereusement erronée.
Courses de légèreté en orbite le
@LightnessRacesinOrbit Pouvez-vous fournir une réponse? Je ne comprends pas pourquoi vous dites que les solutions 2 et 3 doivent être évitées et qu'elles sont dangereusement erronées.
Gladclef
4

En fait, un littéral de constante de chaîne n'est ni un const char * ni un char * mais un char []. C'est assez étrange mais écrit dans les spécifications c ++; Si vous le modifiez, le comportement n'est pas défini car le compilateur peut le stocker dans le segment de code.

dan ionescu
la source
5
Je dirais que c'est const char [] car en tant que rvalue, vous ne pouvez pas le modifier.
fnieto - Fernando Nieto
3

Peut-être que vous pouvez essayer ceci:

void foo(const char* str) 
{
    // Do something
}

foo("Hello")

Ça marche pour moi

Alen Lee
la source
2

Je résous ce problème en ajoutant cette macro au début du code, quelque part. Ou ajoutez-le <iostream>, hehe.

 #define C_TEXT( text ) ((char*)std::string( text ).c_str())
TWOPIR
la source
8
"Ou ajoutez-le dans <iostream>" Quoi?!
Courses de légèreté en orbite le
Il y avait ", hehe" qui a été pour une raison quelconque édité (sous
entend
C_TEXTest bien pour un appel de fonction ( foo(C_TEXT("foo"));), mais réclame un comportement indéfini si la valeur est stockée dans une variable comme char *x = C_TEXT("foo");- toute utilisation de x(en dehors de l'affectation) est un comportement indéfini car la mémoire vers laquelle elle pointe a été libérée.
Martin Bonner soutient Monica le
1

Une raison de ce problème (qui est encore plus difficile à détecter que le problème avec char* str = "some string"- que d'autres ont expliqué) est lorsque vous utilisez constexpr.

constexpr char* str = "some string";

Il semble qu'il se comporterait de la même manière que const char* str, et ne provoquerait donc pas d'avertissement, comme auparavant char*, mais il se comporte plutôt comme char* const str.

Détails

Pointeur constant et pointeur vers une constante. La différence entre const char* stret char* const strpeut être expliquée comme suit.

  1. const char* str: Déclare str comme pointeur vers un caractère const. Cela signifie que les données vers lesquelles ce pointeur pointe vers lui sont constantes. Le pointeur peut être modifié, mais toute tentative de modification des données lèverait une erreur de compilation.
    1. str++ ;: VALIDE . Nous modifions le pointeur, et non les données pointées.
    2. *str = 'a';: INVALIDE . Nous essayons de modifier les données pointées.
  2. char* const str: Déclare str comme pointeur const vers char. Cela signifie que ce point est désormais constant, mais que les données pointées ne le sont pas non plus. Le pointeur ne peut pas être modifié mais nous pouvons modifier les données à l'aide du pointeur.
    1. str++ ;: INVALIDE . Nous essayons de modifier la variable du pointeur, qui est une constante.
    2. *str = 'a';: VALIDE . Nous essayons de modifier les données pointées. Dans notre cas, cela ne provoquera pas d'erreur de compilation, mais entraînera une erreur d'exécution , car la chaîne sera très probablement placée dans une section en lecture seule du binaire compilé. Cette déclaration aurait du sens si nous avions de la mémoire allouée dynamiquement, par exemple. char* const str = new char[5];.
  3. const char* const str: Déclare str comme pointeur const vers un caractère const. Dans ce cas, nous ne pouvons ni modifier le pointeur, ni les données pointées.
    1. str++ ;: INVALIDE . Nous essayons de modifier la variable du pointeur, qui est une constante.
    2. *str = 'a';: INVALIDE . Nous essayons de modifier les données pointées par ce pointeur, qui est également constant.

Dans mon cas, le problème était que je m'attendais constexpr char* strà me comporter comme const char* str, et non char* const str, car visuellement, cela semble plus proche du premier.

De plus, l'avertissement généré pour constexpr char* str = "some string"est légèrement différent de char* str = "some string".

  1. Avertissement du compilateur pour constexpr char* str = "some string":ISO C++11 does not allow conversion from string literal to 'char *const'
  2. Avertissement du compilateur pour char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.

Pointe

Vous pouvez utiliser le convertisseur C gibberish ↔ anglais pour convertir les Cdéclarations en déclarations anglaises facilement compréhensibles, et vice versa. C'est un Coutil unique, et donc ne supporte pas les choses (comme constexpr) qui sont exclusives à C++.

Sahil Singh
la source
0

J'ai aussi le même problème. Et ce que j'ai simplement fait, c'est simplement ajouter const char * au lieu de char *. Et le problème est résolu. Comme d'autres l'ont mentionné ci-dessus, c'est une erreur compatible. C traite les chaînes comme des tableaux de caractères tandis que C ++ les traite comme des tableaux de caractères const.

dilantha111
la source
0

Pour ce que cela vaut, je trouve que cette classe wrapper simple est utile pour convertir des chaînes C ++ en char *:

class StringWrapper {
    std::vector<char> vec;
public:
    StringWrapper(const std::string &str) : vec(str.begin(), str.end()) {
    }

    char *getChars() {
        return &vec[0];
    }
};
bremen_matt
la source
-1

Ce qui suit illustre la solution, affectez votre chaîne à un pointeur de variable vers un tableau constant de char (une chaîne est un pointeur constant vers un tableau constant de char - plus les informations de longueur):

#include <iostream>

void Swap(const char * & left, const char * & right) {
    const char *const temp = left;
    left = right;
    right = temp;
}

int main() {
    const char * x = "Hello"; // These works because you are making a variable
    const char * y = "World"; // pointer to a constant string
    std::cout << "x = " << x << ", y = " << y << '\n';
    Swap(x, y);
    std::cout << "x = " << x << ", y = " << y << '\n';
}
Howard Lovatt
la source