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

16

Que signifie cette erreur? Je ne peux pas le résoudre en aucune façon.

avertissement: conversion obsolète de la constante de chaîne en 'char *' [-Wwrite-strings]

Federico Corazza
la source
Cette question devrait être sur StackOverflow, pas Arduino :)
Vijay Chavda

Réponses:

26

Comme d'habitude, je vais fournir un peu d'informations techniques de fond sur le pourquoi et le comment de cette erreur.

Je vais inspecter quatre façons différentes d'initialiser les chaînes C et voir quelles sont les différences entre elles. Ce sont les quatre voies en question:

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

Maintenant, pour cela, je vais vouloir changer la troisième lettre "i" en "o" pour en faire "Thos est du texte". Cela pourrait, dans tous les cas (on pourrait penser), être réalisé par:

text[2] = 'o';

Voyons maintenant ce que fait chaque façon de déclarer la chaîne et comment cette text[2] = 'o';déclaration affecterait les choses.

D' abord la façon la plus souvent observée: char *text = "This is some text";. Qu'est-ce que cela signifie littéralement? Eh bien, en C, cela signifie littéralement "Créez une variable appelée textqui est un pointeur en lecture-écriture vers ce littéral de chaîne qui est conservé dans un espace en lecture seule (code)". Si l'option est -Wwrite-stringsactivée, vous obtenez un avertissement, comme indiqué dans la question ci-dessus.

Fondamentalement, cela signifie "Avertissement: vous avez essayé de créer une variable qui est en lecture-écriture vers une zone dans laquelle vous ne pouvez pas écrire". Si vous essayez de définir le troisième caractère sur "o", vous essayez en fait d'écrire dans une zone en lecture seule et les choses ne seront pas agréables. Sur un PC traditionnel avec Linux qui se traduit par:

Erreur de segmentation

Maintenant , le second: char text[] = "This is some text";. Littéralement, en C, cela signifie "Créer un tableau de type" char "et l'initialiser avec les données" Ceci est du texte \ 0 ". La taille du tableau sera suffisamment grande pour stocker les données". Donc, cela alloue réellement de la RAM et copie la valeur "This is some text \ 0" dedans au moment de l'exécution. Aucun avertissement, aucune erreur, parfaitement valable. Et la bonne façon de le faire si vous voulez pouvoir modifier les données . Essayons d'exécuter la commande text[2] = 'o':

C'est du texte

Cela a fonctionné parfaitement. Bien.

Maintenant , la troisième voie: const char *text = "This is some text";. Encore une fois, le sens littéral: "Créez une variable appelée" texte "qui est un pointeur en lecture seule vers ces données dans la mémoire en lecture seule.". Notez que le pointeur et les données sont désormais en lecture seule. Aucune erreur, aucun avertissement. Que se passe-t-il si nous essayons d'exécuter notre commande de test? Eh bien, nous ne pouvons pas. Le compilateur est maintenant intelligent et sait que nous essayons de faire quelque chose de mal:

erreur: affectation d'un emplacement en lecture seule '* (texte + 2u)'

Il ne compilera même pas. Les tentatives d'écriture dans la mémoire morte sont désormais protégées car nous avons indiqué au compilateur que notre pointeur est sur la mémoire morte. Bien sûr, il n'a pas avoir à pointer vers la mémoire en lecture seule, mais si vous pointez à la mémoire (RAM) en lecture-écriture que la mémoire sera toujours protégé d'être écrit par le compilateur.

Enfin , la dernière forme: const char text[] = "This is some text";. Encore une fois, comme auparavant, []il alloue un tableau dans la RAM et y copie les données. Cependant, il s'agit maintenant d'un tableau en lecture seule. Vous ne pouvez pas y écrire car le pointeur est marqué comme const. Tenter d'y écrire entraîne:

erreur: affectation d'un emplacement en lecture seule '* (texte + 2u)'

Donc, un bref résumé de notre situation:

Ce formulaire est totalement invalide et doit être évité à tout prix. Cela ouvre la porte à toutes sortes de mauvaises choses qui se produisent:

char *text = "This is some text";

Ce formulaire est le bon formulaire si vous souhaitez rendre les données modifiables:

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

Ce formulaire est le bon formulaire si vous voulez des chaînes qui ne seront pas modifiées:

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

Cette forme semble gaspiller de la RAM mais elle a ses utilisations. Il vaut mieux l'oublier pour l'instant.

const char text[] = "This is some text";
Majenko
la source
6
Il convient de noter que sur les Arduinos (ceux basés sur AVR au moins), les littéraux de chaîne vivent dans la RAM, sauf si vous les déclarez avec une macro comme PROGMEM, PSTR()ou F(). Ainsi, const char text[]n'utilise pas plus de RAM que const char *text.
Edgar Bonet
Teensyduino et de nombreux autres compatibles arduino plus récents placent automatiquement les littéraux de chaîne dans l'espace de code, il vaut donc la peine de vérifier si F () est nécessaire ou non sur votre carte.
Craig.Feied
@ Craig.Feied En général, F () doit être utilisé indépendamment. Ceux qui n'en "ont pas besoin" ont tendance à le définir comme un simple (const char *)(...)casting. Aucun effet réel si la carte n'en a pas besoin, mais une grande économie si vous portez ensuite votre code sur une carte qui en a besoin.
Majenko
5

Pour développer l'excellente réponse de Makenko, il y a une bonne raison pour laquelle le compilateur vous en avertit. Faisons un croquis de test:

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

Nous avons ici deux variables, foo et bar. Je modifie l' un de ceux de setup (), mais vois le résultat:

Thos is some text
Thos is some text

Ils ont tous deux changé!

En fait, si nous regardons les avertissements, nous voyons:

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

Le compilateur sait que c'est douteux, et c'est vrai! La raison en est que le compilateur s'attend (raisonnablement) à ce que les constantes de chaîne ne changent pas (car ce sont des constantes). Ainsi, si vous faites référence à la constante de chaîne "This is some text"plusieurs fois dans votre code, il est autorisé d'allouer la même mémoire à chacun d'eux. Maintenant, si vous en modifiez un, vous les modifiez tous!

Nick Gammon
la source
Sainte Fumée! Qui aurait su ... Est-ce toujours le cas pour les derniers compilateurs ArduinoIDE? Je viens de l'essayer sur un ESP32 et cela provoque des erreurs répétées de GuruMeditation .
not2qubit
@ not2qubit Je viens de tester sur Arduino 1.8.9 et c'est vrai là-bas.
Nick Gammon
Les avertissements sont là pour une raison. Cette fois, j'ai eu: avertissement: ISO C ++ interdit de convertir une constante de chaîne en 'char ' [-Wwrite-strings] char bar = "Ceci est du texte"; - INTERDIT est un mot fort. Comme il vous est interdit de le faire, le compilateur est libre de contourner et de partager la même chaîne sur deux variables. Ne faites pas de choses interdites ! (Lisez également et supprimez les avertissements). :)
Nick Gammon
Donc, si vous rencontrez un code merdique comme celui-ci et que vous souhaitez survivre à la journée. Une déclaration initiale *fooet l' *barutilisation de différentes "constantes" de chaîne empêcheraient-elles cela de se produire? En outre, en quoi est-ce différent de ne pas mettre de chaînes du tout, comme char *foo;:?
not2qubit
1
Différentes constantes pourraient aider, mais personnellement, je n'y mettrais rien et je ne mettrais pas les données de la manière habituelle plus tard (par exemple avec new, strcpyet delete).
Nick Gammon
4

Soit arrêtez d'essayer de passer une constante chaîne où une fonction prend un char*, soit changez la fonction pour qu'elle prenne un à la const char*place.

Les chaînes comme "chaîne aléatoire" sont des constantes.

Ignacio Vazquez-Abrams
la source
Un texte comme "caractères aléatoires" est-il un caractère constant?
Federico Corazza
1
Les littéraux de chaîne sont des constantes de chaîne.
Ignacio Vazquez-Abrams
3

Exemple:

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

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

void loop ()
  {
  }  // end of loop

Avertissement:

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

La fonction fooattend un char * (qu'elle peut donc modifier) ​​mais vous passez un littéral de chaîne, qui ne doit pas être modifié.

Le compilateur vous avertit de ne pas le faire. Étant obsolète, il peut passer d'un avertissement à une erreur dans une future version du compilateur.


Solution: faites en sorte que foo prenne un caractère const *:

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

Je ne comprends pas. Voulez-vous dire ne peut pas être modifié?

Les versions plus anciennes de C (et C ++) vous permettent d'écrire du code comme mon exemple ci-dessus. Vous pouvez créer une fonction (comme foo) qui imprime quelque chose que vous lui transmettez, puis transmettre une chaîne littérale (par exemple. foo ("Hi there!");)

Cependant, une fonction qui prend char *comme argument est autorisée à modifier son argument (c'est-à-dire à modifier Hi there!dans ce cas).

Vous pourriez avoir écrit, par exemple:

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

Malheureusement, en transmettant un littéral, vous avez maintenant potentiellement modifié ce littéral afin que "Salut!" c'est maintenant "Au revoir" qui n'est pas bon. En fait, si vous avez copié dans une chaîne plus longue, vous risquez d'écraser d'autres variables. Ou, sur certaines implémentations, vous obtiendrez une violation d'accès car "Salut!" peut avoir été placé dans la RAM en lecture seule (protégée).

Ainsi, les rédacteurs du compilateur déconseillent progressivement cette utilisation, de sorte que les fonctions auxquelles vous transmettez un littéral doivent déclarer cet argument comme const.

Nick Gammon
la source
Est-ce un problème si je n'utilise pas de pointeur?
Federico Corazza
Quel genre de problème? Cet avertissement particulier concerne la conversion d'une constante de chaîne en un pointeur char *. Peux-tu élaborer?
Nick Gammon
@Nick: Que voulez-vous dire "(..) vous passez un littéral de chaîne, qui ne doit pas être modifié". Je ne comprends pas. Voulez-vous dire can notêtre modifié?
Mads Skjern
J'ai modifié ma réponse. Majenko a couvert la plupart de ces points dans sa réponse.
Nick Gammon
1

J'ai cette erreur de compilation:

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

                         ^

Veuillez remplacer cette ligne:
#define TIME_HEADER "T" // Header tag for serial time sync message

avec cette ligne:
#define TIME_HEADER 'T' // Header tag for serial time sync message

et la compilation se passe bien.

Gin
la source
3
Cette modification modifie la définition d'une chaîne de caractères "T" en un seul caractère avec la valeur du code ASCII pour la lettre majuscule T.
dlu