[Résolu] Résultat du code exécuté dans une char* fonction

Bonjour à tous,

Premier post, j’ai jeté un oeil aux règles, et j’espère que je vais réussir à en respecter au maximum.

Voilà mon problème : pour un même morceau de code, je n’obtiens pas tout le temps le même résultat si ce code est dans une fonction char* ou dans la fonction loop.

Voilà le code que j’ai :

//chargement des librairies
#include <Time.h>


//configuration du temps
const int heureInit = 22;
const int minuteInit = 50;
const int secondeInit = 20;
const int jourInit = 7;
const int moisInit = 2;
const int anneeInit = 2012;

void setup()
{
  // configuration de la liaison série
  Serial.begin(9600);
  
  //initialisation du temps
  Serial.print("Initializing time...");
  setTime(heureInit,minuteInit,secondeInit,jourInit,moisInit,anneeInit);
  Serial.println("initialization done.");
}

void loop ()
{
  time_t t=now();
  
//  //******************************************************  
//  //morceau de code posant problème
//  char result[20];
//  char buffer[5];
//  strcpy(result,"");
//  //récupération du jour
//  itoa(day(t),buffer,10);
//  strcat(result,buffer);
//  strcat(result,"/");
//  //récupération du mois
//  itoa(month(t),buffer,10);
//  strcat(result,buffer);
//  strcat(result,"/");
//  //récupération de l'année
//  itoa(year(t),buffer,10);
//  strcat(result,buffer);
//  
//  //affichage du résultat, sans fonction
//  Serial.println(result);

  
  //affichage du résultat, avec fonction
  Serial.println(TimetoChar(t));
  
  
  Serial.println("test");
  delay (2000);
}


//******************************************************
char* TimetoChar (time_t t){
/*
fonction renvoyant le temps t sous la forme J/M/AAAA H:m:s, au format char

dev todo : ne fonctionne pas encore
*/
  //******************************************************  
  //morceau de code posant problème
  char result[20];
  char buffer[5];
  strcpy(result,"");
  //récupération du jour
  itoa(day(t),buffer,10);
  strcat(result,buffer);
  strcat(result,"/");
  //récupération du mois
  itoa(month(t),buffer,10);
  strcat(result,buffer);
  strcat(result,"/");
  //récupération de l'année
  itoa(year(t),buffer,10);
  strcat(result,buffer);

  return result;
}

Quand je commente le code faisant le même travail que celui de la fonction TimetoChar dans la fonction loop, j’obtiens le résultat fourni en pièce-jointe. Ça ne correspond pas vraiment à ce que j’attendais, mais je ne comprends pas pourquoi.

Si je laisse le code s’éxecuter dans la fonction loop (en enlevant les commentaires) et dans la fonction TimetoChar, j’obtiens :

Initializing time...initialization done.
7/2/2012
7/2/2012
test
7/2/2012
7/2/2012
test
7/2/2012
7/2/2012
test

Ce qui me semble cohérent.

Est-ce que quelqu’un a une solution/explication à mon problème ?

Capture du 2013-12-09 23:57:55.png

Bonjour,

Ta variable char* dans ta fonction est une variable locale temporaire, définie sur la pile d'exécution. Après l'instruction return, le contenu de cette variable est devenu invalide.

Pour que cela fonctionne, tu as plusieurs possibilités - passer ta valeur de retour comme paramètre de fonction (et l'appelant doit lui-même réserver l'espace-mémoire de la chaîne de caractères) - mettre cette variable en globale ou static dans la fonction - allouer de la mémoire du heap avec malloc(), mais l'appelant doit ensuite appeler free(). Cette dernière solution n'est pas recommandée car tu vas très vite fragmenter la RAM et faire planter l'Arduino.

j’ai testé ton prog et oui sa bug mais XavierMiller à raison si tu déclare result en global ça fonctionne :

//chargement des librairies
#include <Time.h>

//configuration du temps
const int heureInit = 22;
const int minuteInit = 50;
const int secondeInit = 20;
const int jourInit = 7;
const int moisInit = 2;
const int anneeInit = 2012;
char result[20];

void setup()
{
  // configuration de la liaison série
  Serial.begin(9600);

  //initialisation du temps
  Serial.print("Initializing time...");
  setTime(heureInit,minuteInit,secondeInit,jourInit,moisInit,anneeInit);
  Serial.println("initialization done.");
}

void loop ()
{
  time_t t=now();
  
//  //******************************************************  
//  //morceau de code posant problème
  
  char buffer[5];
  strcpy(result,"");
  //récupération du jour
  itoa(day(t),buffer,10);
  strcat(result,buffer);
  strcat(result,"/");
  //récupération du mois
  itoa(month(t),buffer,10);
  strcat(result,buffer);
  strcat(result,"/");
  //récupération de l'année
  itoa(year(t),buffer,10);
  strcat(result,buffer);
  
  //affichage du résultat, sans fonction
  Serial.print("loop = ");
  Serial.print(result);

  
  //affichage du résultat, avec fonction
  Serial.print(" / TimetoChar(t) = ");
  Serial.println(TimetoChar(t));

    
  delay (2000);
}


//******************************************************
char* TimetoChar (time_t t){
/*
fonction renvoyant le temps t sous la forme J/M/AAAA H:m:s, au format char

dev todo : ne fonctionne pas encore
*/
  //******************************************************  
  //morceau de code posant problème
  
  char buffer[5];
  
  //récupération du jour
  strcpy(result,"");
  itoa(day(t),buffer,10);
  strcat(result,buffer);
  strcat(result,"/");
  //récupération du mois
  itoa(month(t),buffer,10);
  strcat(result,buffer);
  strcat(result,"/");
  //récupération de l'année
  itoa(year(t),buffer,10);
  strcat(result,buffer);

  return result;
   
}

les mystères de la programmation …

Ce n’est pas un mystère, c’est spécifié dans la norme C/C++ :wink:

Je vois rien de mystérieux non plus :)

Je ne suis pas fort en programmation C++ j'ai plus bricole en basic

Quelque part c'est logique : tu retournes un pointeur qui pointe vers une variable locale de la fonction, donc quelque part on "sent" que c'est pas rigoureux.

Merci pour ces réponses. Je découvre le C++ et tout ce que je connaissais à peu près c'était le python. Autant dire que ces histoires de pointeurs, et la déclaration de variable à tout va, c'est nouveau.

Je pense que je vais faire une fonction char, et non char* pour éviter les pointeurs.

XavierMiller:
Pour que cela fonctionne, tu as plusieurs possibilités

  • passer ta valeur de retour comme paramètre de fonction (et l’appelant doit lui-même réserver l’espace-mémoire de la chaîne de caractères)
  • mettre cette variable en globale ou static dans la fonction
  • allouer de la mémoire du heap avec malloc(), mais l’appelant doit ensuite appeler free(). Cette dernière solution n’est pas recommandée car tu vas très vite fragmenter la RAM et faire planter l’Arduino.

J’ai essayé d’avancer dans le sens de la première possibilité de XavierMiller, et voilà le code que j’ai maintenant et qui semble fonctionner :

//chargement des librairies
#include <Time.h>


//configuration du temps
const int heureInit = 22;
const int minuteInit = 50;
const int secondeInit = 20;
const int jourInit = 17;
const int moisInit = 12;
const int anneeInit = 2012;

void setup()
{
  // configuration de la liaison série
  Serial.begin(9600);
  
  //initialisation du temps
  Serial.print("Initializing time...");
  setTime(heureInit,minuteInit,secondeInit,jourInit,moisInit,anneeInit);
  Serial.println("initialization done.");
}

void loop ()
{
  time_t t=now();
  
  //affichage du résultat, avec fonction
  char texte[20]="";
  TimetoChar(t,texte);
  Serial.println(texte);
  
  
  
  Serial.println("test");
  delay (2000);
}


//******************************************************
void TimetoChar (time_t t, char* result){
/*
fonction renvoyant le temps t sous la forme J/M/AAAA h:m:s, au format char, dans la variable result

dev todo : ne prend pas en compte la taille disponible dans la variable result
*/
  //création de variables temporaire
  char buffer[5]="";
  char buffer2[20]="";
  
  //récupération du jour
  itoa(day(t),buffer,10);
  strcat(buffer2,buffer);
  strcat(buffer2,"/");
  //récupération du mois
  itoa(month(t),buffer,10);
  strcat(buffer2,buffer);
  strcat(buffer2,"/");
  //récupération de l'année
  itoa(year(t),buffer,10);
  strcat(buffer2,buffer);
  strcat(buffer2," ");
  //récupération de l'heure
  itoa(hour(t),buffer,10);
  strcat(buffer2,buffer);
  strcat(buffer2,":");
  //récupération de la minute
  itoa(minute(t),buffer,10);
  strcat(buffer2,buffer);
  strcat(buffer2,":");
  //récupération de la seconde
  itoa(second(t),buffer,10);
  strcat(buffer2,buffer);
  
  //écriture du résultat dans la variable result
  strcpy(result,buffer2);
}

Comme je suis un bon vrai débutant, je ne suis même pas sur que c’est ce que tu voulais dire XavierMiller, mais j’en ai l’impression.

Sinon, tout marche bien tant que la variable que je passe à ma fonction TimetoChar fait une taille suffisante (soit 20 caractères). Si ça fait moins, ça plante, et ça ne m’étonne pas, mais y a-t-il une solution pour vérifier la taille dispo et ne rien faire (ou renvoyer un message d’erreur) si la variable fournie n’est pas assez grande ?

je pige pas tout ... mais si tu supprime à la fin de la fonction le transfert de buffer2 dans result

  //écriture du résultat dans la variable result
 // strcpy(result,buffer2);

et qu'au début de la fonction du passe buffer2 en paramètre ça fonctionne aussi

//******************************************************
void TimetoChar (time_t t, char buffer2[20]){

comment avec la fonction "TimetoChar(t,texte); " texte récupère la valeur de buffer2 je pensait que c'était l'inverse ... pff je vais aller faire un tour dans la doc du c++ et les fonctions ...

en tout cas c'est super intéressent de programmer

fifi82: je pige pas tout ... mais si tu supprime à la fin de la fonction le transfert de buffer2 dans result

  //écriture du résultat dans la variable result
 // strcpy(result,buffer2);

et qu'au début de la fonction du passe buffer2 en paramètre ça fonctionne aussi

//******************************************************
void TimetoChar (time_t t, char buffer2[20]){

comment avec la fonction "TimetoChar(t,texte); " texte récupère la valeur de buffer2 je pensait que c'était l'inverse

bah le but c'est de modifier texte, pas buffer2 qui en plus est locale. La fontion TimetoChar accepte un pointeur en second argument, donc c'est directement texte qui est modifié.

ThibaultL: Si ça fait moins, ça plante, et ça ne m'étonne pas, mais y a-t-il une solution pour vérifier la taille dispo et ne rien faire (ou renvoyer un message d'erreur) si la variable fournie n'est pas assez grande ?

sizeof() ;)

Je viens d'essayer sizeof et quand je laisse un pointeur en paramètre de fonction :

void TimetoChar (time_t t, char* result){
  Serial.println(sizeof(result));
}

J'obtiens une taille de deux, taille de l'adresse du pointeur. Ça semble normal vu ce que j'ai lu sur internet.

Par contre, je ne comprends pas pourquoi en déclarant ma fonction de la façon suivante :

void TimetoChar (time_t t, char result[20])

J'obtiens toujours une taille de deux !

Pour info, j'ai la loop suivante :

void loop ()
{
  time_t t=now();
  
  //affichage du résultat, avec fonction
  char texte[20];
  Serial.println(sizeof(texte));
  TimetoChar(t,texte);
  Serial.println(texte);

  Serial.println(sizeof(texte));
  
  Serial.println("test");
  delay (2000);
}

Et dans cette loop, la fonction sizeof renvoie bien une taille de 20 pour la variable "texte".

Au pire, je ne vais pas faire de vérification sur la taille de la variable passée à la fonction, mais ça ne me plait qu'à moitié :(

J'en suis pas sûr mais je crois que c'est toujours le même problème : tu détermines la taille d'une variable temporaire. D'où le fait que ça marche dans la loop. Dans ta fonction, essaye plutôt : Serial.println(sizeof(*result)); ==> taille de la variable pointée et pas du pointeur ;)

J'avais déjà essayé il me semble, mais je viens de refaire le test et j'obtiens 1, taille du premier caractère du pointé j'imagine.

C'est pas très grave, je ne vais pas non plus faire un truc de fou sachant que la taille de ma variable passée est fixe, de 20, je vais partir là-dessus sans vérification.

un sizeof d’un (char) est 1.
Pour obtenir la taille d’une chaîne de caractères, c’est strlen.