Chaines et gestion mémoire

Bonjour,
Je travaille sur les échanges de données sur le port série ; carte UNO + shield Adafruit LCD + 5 boutons en I²C.

Depuis plusieurs heures je cherche à comprendre comment fonctionnent les variables chaînes de caractères.
Voir croquis d'essai ci-dessous.

A la ligne 11, j'initialise une variable char mess[] = "Initialisation"; que j'affiche à la ligne 24, dans le setup(). OK.

Dans la boucle loop() je change le texte de cette même variable à plusieurs reprises après les if, selon le bouton sollicité. OK aussi, le bon texte s'affiche pour chaque bouton.

Par contre, en ligne 67 je lance la même commande d'affichage. Et là, il m'affiche le mot "Initialisation", c'est à dire le texte de la première déclaration, alors que je m'attendais à une répétition du dernier texte déclaré !... Comment est-ce possible ?
A chaque nouvelle déclaration (char mess[] = "xxxx";), le système occupe-t-il un nouvel emplacement mémoire ? Si oui, comment le libérer après chaque utilisation pour éviter de la saturer ?

Merci d'avance pour vos éclaircissements.
@+
Pierre

Arduino_Essai_19-11-21.ino (2.14 KB)

Bonjour,

Quand tu fait char mess[] = "Initialisation"; le compilateur crée un tableau de caractères qui contient les caractères initialisation et terminé par 0. Cette syntaxe n'est valable que pour ca définition de la variable.

Quand par la suite tu fais de nouveau char mess[] = "xxxxxxx" tu définis une autre variable qui n'a rien à voir avec la variable globale que tu as défini précédemment. Donc son contenu n'est pas modifié.

Remarque:
au lieu de faire

        char mess[] = "Bouton RETOUR   ";
        Serial.println(mess);

Tu peux directement faire

        Serial.println("Bouton RETOUR   ");
char mess[] = "Initialisation";       // message à afficher

Tu redéclare mess à chaque fois :

char mess[] = "Bouton RETOUR ";

Il faut réutiliser la même chaîne :

strcpy(mess, "Bouton RETOUR ";

Mais ta chaîne est trop courte. Ceci serait mieux :

char mess[20] = "Initialisation";       // message à afficher

Et ceci serait encore mieux (sans recopie) :

const char *mess = "Initialisation";       // message à afficher

// plus loin :

        mess = "Bouton RETOUR";
[/quote]

Merci à vous 2 !

En fait, c'était un exemple pour comprendre, je n'utiliserai pas ma variable mess[] pour l'envoyer à la console (Serial.println...) mais pour l'utiliser dans un autre contexte. Mais j'ai bien compris la question de la variable globale différente de la variable locale utilisée plus loin. Je suis étonné que le compilateur n'alerte pas pour ce genre de doublon ! Bon, il faut le savoir....

Par contre, que signifie exactement la syntaxe : const char *mess = "Initialisation"; (à part que c'est une constante char) ? Le rôle de l'astérisque ?

@+

const char *mess = "Initialisation";

mess est une variable de type pointeur, contenant l'adresse d'une chaîne de caractère.

L'intérêt est que lors de l'affectation on ne se soucie pas de la longueur.

mess = "chaîne";
mess = "très longuuuuuuuuuuuuuuuuuue chaîne";
char s[] = "abcdef";
mess = s;

Les pointeurs sont très souples.

Merci hbachetti, je vais étudier tout ça sérieusement !

J'ai du mal avec les pointeurs...

Si tu définis

const char *mess = "Initialisation";

Ça veut dire qu'il y aura un 0 en fin de chaîne, après le dernier caractère, donc 15 octets plus loin en mémoire. Correct ?
Maintenant, si tu dis un peu plus loin

mess = "chaine";

Les nouveaux caractères remplacent les anciens et un 0 se place après le 5ème. Toujours bon ?
Mais le reste de la mémoire peut être utilisé pour d'autres variables, notamment l'espace disponible après les caractères de la première chaîne "Initialisation".
Donc si je fais ensuite

mess = "très longuuuuuuuuuuuuuuuuuue chaine";

Les variables situées après la chaîne initiale risquent d'être corrompues, non ?

Ça veut dire qu'il y aura un ZÉRO en fin de chaîne, après le dernier caractère, donc 15 octets plus loin en mémoire. Correct ?

Bien entendu. Avec ou sans pointeur la chaîne "Initialisation" est terminée par zéro.
"Initialisation" est une chaîne quelque part en mémoire, elle n'est pas associée à un symbole.
Le pointeur contient simplement l'adresse de cette chaîne.

Les nouveaux caractères remplacent les anciens et un 0 se place après le 5ème. Toujours bon

Non. On affecte au pointeur l'adresse d'une autre chaîne terminée par ZÉRO.

Donc si je fais ensuite

mess = "très longuuuuuuuuuuuuuuuuuue chaine";

Les variables situées après la chaîne initiale risquent d'être corrompues, non ?

Non. On affecte au pointeur l'adresse d'une troisième chaîne terminée par ZÉRO.

Cela fait en tout trois chaînes distinctes en mémoire, plus un pointeur.

Ok, je comprends mieux, merci

Il faut comprendre une chose :

// cette chaîne est associée (au lien) à un symbole msg1.

char msg1[] = "blabla";

// cette chaîne n'est pas associée à un symbole, bien qu'elle existe en mémoire.
// Seul le pointeur msg1 existe en tant que symbole.

char *msg1 = "blabla";

Bonjour,
mais du coup, elles sont dans quelle mémoire les chaînes ?

Sur ATMEGA si tu ne les mets pas toi-même en FLASH (PROGMEM) elles sont en RAM.

hbachetti:
Sur ATMEGA si tu ne les mets pas toi-même en FLASH (PROGMEM) elles sont en RAM.

Plus précisément elles sont en flash et sont recopiées en RAM au démarrage.

Oui, bien sûr.
Je trouve ça dommage d'ailleurs. Sur pas mal d'autres processeurs (STM32, ARM, etc.), il suffit de préciser const pour que la variable soit en FLASH, sans recopie au démarrage.

Oui, effectivement quand j'ai commencé avec l'arduino, j'ai été très surpris de voir qu'on ne pouvait pas adresser directement les constantes en flash et quelles étaient recopiées en ram.