Arrays of strings : utilisation correcte des tableaux de caractères

Bonjour
Dans mon main, j'ai besoin d'envoyer 2 tableaux de caractères à ma librairie de publication MQTT.

Dans le main j'ai le code :

const int sensorNumber = 3;
const char* sensorName[] = {"temperature", "moisture", "noiselevel"};
char* sensorValue[sensorNumber];

void setup()
{
    Serial.begin(115200);
    ...
}

void loop()
{
    sensorValue[0] = "22.5";
    sensorValue[1] = "44.5";
    sensorValue[2] = "250";
    ...
}

Dans cette exemple, je code en dur les valeurs des sensors car le but est juste de tester ma librairie de publication MQTT. Par la suite, les valeurs seront récupérées grâce aux librairies spécifiques à chaque sensor.
J'ai déclaré sensorValue en char* car même si dans cette exemple (température, moisture, noiselevel) on a des valeurs numériques, on peut imaginer un sensor qui retourne une chaine de caractères.
Qd je compile ça, j'ai un warning pour les 3 affectations sensorValue[0] = "22.5" avec le message suivant : "warning: deprecated conversion from string constant to 'char*'".
J'ai fait la modif suivante :
sensorValue[1] = (char*)"22.5";
Dans ce cas, je n'ai plus de warning mais je ne suis pas sur que ce soit la "bonne" façon de faire.
Merci de vos retours
Bonne soirée

Bonjour,

Il n'y a pas de raison qu'il y ait un message d'erreur. Quelle est ta version de l'ide?
Par contre cette méthode n'est pas adaptée si la valeur est variable.

kamill:
Il n'y a pas de raison qu'il y ait un message d'erreur.

Bien sûr que si.
le chaines entre guillemets sont des const char*
et sensorValue est déclaré char* (sans le const)
d'où le warning.
Il faut déclarer sensorValue comme const char*

biggil:
Bien sûr que si.
le chaines entre guillemets sont des const char*
et sensorValue est déclaré char* (sans le const)
d’où le warning.
Il faut déclarer sensorValue comme const char*

Bonjour
Merci pour vos réponses.
Effectivement le “22.5” est vu comme un const char* mais je ne vois pas en quoi c’est une faute d’affecter un char* avec la valeur d’un const char*. Je suis preneur de vos éclaircissements.

Vous avez un warning, pas une erreur car vous perdez cette notion de constante. Si plus tard dans le code vous essayez de bidouiller la chaîne elle même, le compilateur ne pourra pas vous prévenir que ce n'est pas une bonne idée

si à terme vous souhaitez que ce soit variable, il faut sans doute réserver de la mémoire pour vos chaînes plutôt que juste un pointeur (bien que je ne comprenne pas pourquoi ce ne sont pas plutôt des float que vous transformerez plus tard en ASCII)

Pour le moment, je code en dur les valeurs des capteurs juste pour tester ma lib MQTT mais pour la suite je vais avoir le code suivant :

sensorValue[0] = GetTemperatureAM2302();

cette fonction étant du type : float GetTemperatureAM2302()

Du coup pour ne pas avoir d'erreur, je devrais écrire :
sensorValue[0] = (string)GetTemperatureAM2302();

J'ai déclaré mon tableau de valeurs de capteurs en char* pour être le plus générique possible pour ma lib vis à vis des capteurs que je vais pouvoir utiliser par la suite

Merci

il vaut mieux ne pas utiliser la classe String (qui prend un S majuscule) sur un petit microprocesseur. gardez le float et transformez en ASCII les valeurs juste au moment où vous en aurez besoin avec la fonction print ou les fonctions de traitement des cStrings classiques

J-M-L:
Vous avez un warning, pas une erreur car vous perdez cette notion de constante. Si plus tard dans le code vous essayez de bidouiller la chaîne elle même, le compilateur ne pourra pas vous prévenir que ce n'est pas une bonne idée

si à terme vous souhaitez que ce soit variable, il faut sans doute réserver de la mémoire pour vos chaînes plutôt que juste un pointeur (bien que je ne comprenne pas pourquoi ce ne sont pas plutôt des float que vous transformerez plus tard en ASCII)

Je ne suis pas un pro de la programmation, donc vos suggestions sont les bienvenues car ça me fait progresser.
Ma librairie MQTT permet de publier des données (valeurs de capteurs) sur Live Object. Pour rendre ma lib le plus générique possible, je lui passe en paramètres ces 2 tableaux (1 : type de capteur et 2 : valeur de capteur). Pour le moment, les différents capteurs que j'ai retournent plutôt des float mais je me suis dit que je serait plus générique en déclarant en char* mon tableau de valeur pour le cas où par exemple j'utilise un capteur qui retournerait un caractère ou une chaine de caractères.
Ce n'est peut être pas une bonne idée, je suis preneur des votres.
Merci

par exemple regardez cela:

float f = 12.345;
char message[30]; // prévoir assez de place pour ce que vous voulez y stocker + 1 caractère en plus (le '\0' à la fin)

#define precision 3

void setup() {
  Serial.begin(115200);
  Serial.print("f = "); Serial.println(f, precision);


  dtostrf(f, 1, precision, message); // https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga060c998e77fb5fc0d3168b3ce8771d42
  Serial.print("f = "); Serial.println(message);
}

void loop() {}

ça envoie sur la console série le même message ASCII mais c'est géré de 2 façons différentes.

bigorne:
Effectivement le "22.5" est vu comme un const char* mais je ne vois pas en quoi c'est une faute d'affecter un char* avec la valeur d'un const char*. Je suis preneur de vos éclaircissements.

C'est une règle de base du C++.
le mot-clé const n'est pas seulement décoratif, il change le type de la variable: char* et const char* sont des types différents.

Dans ton exemple, tu déclares char* ptr = "22.5";
Quand il voit le membre de droite "22.5", le compilateur réserve une zone de 5 octets (4c aractères plus le 0 final) dans la zone de DATAS du programme, et il y met '2' '2' '.' '5' '\0'
Par précaution, il voit ça comme un chaîne constante, c-à-d non modifiable. Le type de ce truc est donc const char*.
Puis il voit que tu copies ce pointeur dans un pointeur char*, qui permet de modifier la chaîne.
C'est à dire que que tu peux tenter d'augmenter le longueur de de la chaîne, en faisant pas exemple

strcat ( ptr, "une suite" );

Si tu fais ça, la suite va déborder des 5 octets réservés et écraser d'autres données, ce qui va probablement aboutir à un bug, difficile à trouver qui plus est.

Alors le compilo te mets un warning, il est sympa, il te fait confiance - en fait il te dit "tu prends tes responsabilités, je m'en lave les mains".
Le compilateur est ton ami, il est extrèmement intelligent, écoute ce qu'il te dit !

biggil:
C'est une règle de base du C++.
le mot-clé const n'est pas seulement décoratif, il change le type de la variable: char* et const char* sont des types différents.

Dans ton exemple, tu déclares char* ptr = "22.5";
Quand il voit le membre de droite "22.5", le compilateur réserve une zone de 5 octets (4c aractères plus le 0 final) dans la zone de DATAS du programme, et il y met '2' '2' '.' '5' '\0'
Par précaution, il voit ça comme un chaîne constante, c-à-d non modifiable. Le type de ce truc est donc const char*.
Puis il voit que tu copies ce pointeur dans un pointeur char*, qui permet de modifier la chaîne.
C'est à dire que que tu peux tenter d'augmenter le longueur de de la chaîne, en faisant pas exemple

strcat ( ptr, "une suite" );

Si tu fais ça, la suite va déborder des 5 octets réservés et écraser d'autres données, ce qui va probablement aboutir à un bug, difficile à trouver qui plus est.

Alors le compilo te mets un warning, il est sympa, il te fait confiance - en fait il te dit "tu prends tes responsabilités, je m'en lave les mains".
Le compilateur est ton ami, il est extrèmement intelligent, écoute ce qu'il te dit !

Ok je pense avoir compris. C'est parce que mon char* prend l'adresse de mon const char*. Du coup on a 2 pointeurs qui pointent sur la même zone mémoire dont l'original est un const char* et le suivant un char*.
Il y a effectivement un risque que mon char* puisse modifier la valeur de la zone pointée.
Je vais regarder les exemples de J-M-L plus en détails car je n'ai pas trop compris pour le moment.
Merci

J-M-L:
par exemple regardez cela:

float f = 12.345;

char message[30]; // prévoir assez de place pour ce que vous voulez y stocker + 1 caractère en plus (le ‘\0’ à la fin)

#define precision 3

void setup() {
  Serial.begin(115200);
  Serial.print("f = "); Serial.println(f, precision);

dtostrf(f, 1, precision, message); // avr-libc: <stdlib.h>: General utilities
  Serial.print("f = "); Serial.println(message);
}

void loop() {}




ça envoie sur la console série le même message ASCII mais c'est géré de 2 façons différentes.

Bonjour
J’ai essayé d’utiliser la fonction dtostrf mais sans succès. Voici mon code

const int sensorNumber = 3;
const char* sensorName[] = {"temperature", "moisture", "noiselevel"};
char* sensorValue[sensorNumber];

void setup()
{
    Serial.begin(115200);
    ...
}

void loop()
{
    dtostrf(GetTemperatureAM2302(), 1, 3, sensorValue[0]);
    ...
}

Sachant que GetTemperatureAM2302() retourne un float.
Je n’ai pas d’erreur à la compilation mais le code ne fonctionne pas. Je dois déborder sur de l’espace mémoire.

Il y a quelque chose qui m’échappe. Quand je déclare mon sensorValue avec char* sensorValue[sensorNumber]; je déclare bien un pointeur sur un tableau de char qui aura 3 cases. Mais jamais je ne réserve de taille mémoire pour le contenu de chaque case !

Merci de vos retours

J'ai trouvé une solution pour réserver la bonne taille mémoire à ce que pointe mon tableaux de pointeurs. Voici le code :

const int sensorNumber = 3;
const char* sensorName[] = {"temperature", "moisture", "noiselevel"};
char* sensorValue[sensorNumber];

void setup()
{
    Serial.begin(115200);
    sensorValue[0] = new char[30];
    sensorValue[1] = new char[30];
    sensorValue[2] = new char[30];

    ...
}

void loop()
{
    dtostrf(GetTemperatureAM2302(), 1, 3, sensorValue[0]);
    ...
}

Comme ceci je n'ai plus de débordement et ça fonctionne.

Prends exemple sur J-M-L et mets ton code entre balises CODE (c’est la touche </> lorsque tu fais un reply ou un preview). Fais-le pour tous tes messages, sinon ton fil de discussion risque de disparaître…

lesept:
Prends exemple sur J-M-L et mets ton code entre balises CODE (c’est la touche </> lorsque tu fais un reply ou un preview). Fais-le pour tous tes messages, sinon ton fil de discussion risque de disparaître…

Ok c’est noté et c’est fait sur l’ensemble des messages
Merci du conseil