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]
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]
Réponses:
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 text
qui 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-strings
activé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";
PROGMEM
, PSTR()
ou F()
. Ainsi, const char text[]
n'utilise pas plus de RAM que const char *text
.
(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.
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!
*foo
et l' *bar
utilisation 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;
:?
new
, strcpy
et delete
).
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.
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 foo
attend 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
.
can not
être modifié?
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.