Enigme sur les fonctions qui retournent qui retourne une valeur sans return

Bonjour,
Je suis enseignant et j'ai posé un problème à mes élèves.
Le principe était simple, j'ai créé une simulation sous tinkercad, composé d'un simple servo-moteur et un capteur ultra son. Ceci était pour simuler un radar. Ce montage devait faire pivoter le capteur tout les 10° de 0° à 180° et revenir à son point de départ. Il devait renvoyer deux valeurs séparées par une virgule (l'angle de rotation et la distance). La mesure de distance devait se faire dans une fonction.
Voici le lien de la simulation :

Voici le code que m'a proposé un élève :

#include <Servo.h> 
//Librairie du servo
Servo monServo; //Déclaration de monServo qui va être le servoMoteur du circuit 
int US;			//Déclaration de US en nombre
float dureeUS;	//Déclaration de dureeUS en nombre long
int distanceCM;	//Déclaration de US en nombre
int distance;
int brocheTrigger = 13;
int brocheEcho = 12;
void setup()
{
  Serial.begin(9600); 
 pinMode(13, OUTPUT);	// Définir que la broche 13 est une sortie sur la carte arduino
 pinMode(12, INPUT);	// Définir que la broche 12 est une entrée sur la carte arduino
  monServo.attach(11);	// Définir que la broche 11 est l'entree du servoMoteur
  
}

int captureDistance(int brocheTrigger, int brocheEcho) //Décalration de la Fonction captureDistance qui demande 2 nombre(numéro de broche d'entrée et de sortie) pour fonctionner
{
  digitalWrite(brocheTrigger,0); //"Ecriture" de 1 sur la brocheTrigger ce qui va permettre de désactiver le capteur d'ultrason  
  delayMicroseconds(2);			 //Delai de 2ms
  digitalWrite(brocheTrigger,1); //"Ecriture" de 1 sur la brocheTrigger ce qui va permettre d'activer le capteur d'ultrason
  delayMicroseconds(10);		 //Delai de 10ms
  digitalWrite(brocheTrigger,0); //"Ecriture" de 1 sur la brocheTrigger ce qui va permettre de désactiver le capteur d'ultrason   
  
  dureeUS = pulseIn(brocheEcho,1);	//Définir dureeUS comme lecture de la brocheEcho et de savoir la durée en ms
  delay(1000);					  	//Delai de 1s
  distanceCM = (dureeUS*0.0334)/2;  //Définir distanceCM qui est le resultat de (dureeUS*0.0334)/2
}

int nextTickMotor(int angle)
{
  monServo.write(angle);
  
}

void loop()

{

for(int i=0; i<190; i=i+10)

{

   nextTickMotor( i);

   distance=captureDistance(brocheTrigger,brocheEcho);

  Serial.print(i);

  Serial.print(",");

  Serial.println(distance);

}

for(int i=170; i>=0; i=i-10)

{

   nextTickMotor( i);

   distance=captureDistance(brocheTrigger,brocheEcho);

  Serial.print(i);

  Serial.print(",");

  Serial.println(distance);

}

}

Il n'y a pas de "return distanceCM;".
Et pourtant ça fonctionne.
J'ai compiler le programme sur l'arduino ide et il ne me retourne pas d'érreur.
Est ce que cela veut dire que la fonction retourne la dernière valeur calculée, lorsqu'il n'y a pas de return ?
Je n'ai rien trouvé la dessus sur internet.
Est ce que quelqu'un peut m'éclairer ?
Merci

La variable est déclaré en globale.. donc elle est vue et modifiable partout dans le sketch, tout a fait normal que la valeur récupéré soit la bonne

//Librairie du servo
Servo monServo; //Déclaration de monServo qui va être le servoMoteur du circuit
int US;			//Déclaration de US en nombre
float dureeUS;	//Déclaration de dureeUS en nombre long


int distanceCM;	//Déclaration de US en nombre *******  ici ******* Arzou


int distance;
int brocheTrigger = 13;
int brocheEcho = 12;
void setup()

Je suis d'accord avec toi, mais ce qui m'intrigue, c'est que l'envois sur le port série se fait avec la variable "distance" et que dans la fonction la lecture de la distance se fait par "distanceCM" et pourtant l'appel à la fonction

distance=captureDistance(brocheTrigger,brocheEcho);

récupère dans "distance" la valeur, hors il n'y a pas de return ?
A aucun moment il ne met

sistance=distanceCM;

distance=distanceCM;
Pardon

   distance=captureDistance(brocheTrigger,brocheEcho);

La valeur retournée est placée dans distance, pas dans distanceCM.
Donc si distance prend la bonne valeur, il est probable que ce soit dû au hasard.

En général la valeur retournée est placée dans un registre, il suffit que ce registre aie servi pour le calcul pour que la valeur retournée soit OK.

Toute fonction retournant une valeur doit impérativement la retourner, sinon c'est compter sur la chance.

D'autre part déclarer la variable distanceCM en global alors qu'elle est utilisée uniquement dans captureDistance() est une erreur grossière. Elle devrait être locale à la fonction.

GillesT:
Est ce que cela veut dire que la fonction retourne la dernière valeur calculée, lorsqu'il n'y a pas de return ?

Il suffit de tester : tu changes un peu la fin de la fonction, par exemple en ajoutant

int test = 42;

et tu regardes ce que devient la variable distance

Arzou:
La variable est déclaré en globale.. donc elle est vue et modifiable partout dans le sketch, tout a fait normal que la valeur récupéré soit la bonne

Non ce n'est pas cela

Bien que les variables soient globales, c'est distanceCM qui est affecté dans la fonction et pas distance

D'une part le compilateur va vous mettre un warning si vous les avez activé

[color=orange]
sketch_mar23a.ino: In function 'int captureDistance(int, int)':
sketch_mar23a.ino:31:1: warning: no return statement in function returning non-void [-Wreturn-type][/color]

et ça ne va pas marcher dans un vrai programme sur Arduino. pour vous en convaincre compilez et testez cela dans le moniteur série ouvert à 115200 bauds

int distance_cm;
int distance;

int mesureDistance()
{
  distance_cm = analogRead(A0) * random(1, 33);
}
void setup() {
  Serial.begin(115200);
}

void loop() {
  distance = mesureDistance();
  Serial.print(distance_cm);
  Serial.write('\t');
  Serial.println(distance);
  delay(2000);
}

(j'ai pas testé mais je pense que vous verrez varier le premier chiffre et le second restera à 0).

je pense que le comportement que vous avez dans tinkerCad vient simplement du fait que le simulateur sans doute retourne dans distance un certain nombre d'octets qui sont au sommet de la pile, hors le dernier calcul effectué était la distance et donc il récupère cela pour l'affecter dans la valeur. Vous pourriez sans doute vous prouver que c'est le comportement en rajoutant à la fin de votre code dans le simulateur

 distanceCM = (dureeUS*0.0334)/2;  //Définir distanceCM qui est le résultat de (dureeUS*0.0334)/2
  long x = 111*2; // RAJOUTER CELA

je suis prêt à parier que la valeur qu'il va afficher contiendra 222 quelque part

==> oublier le return contrevient à la promesse que vous aviez faite au compilateur qu'il y aurait quelque chose de renvoyé et donc le comportement obtenu est indéfini. C'est normal de ne pas voir le même comportement suivant le type de plateforme ou compilateur utilisé.

En activant les warnings du compilateur on met toutes les chances de son côté :
Fichier/préférences :

  • afficher les résultats détaillés pendant : compilation (téléversement éventuellement)
  • avertissements du compilateur : tout

Exemple :

  // incorrect
  if (request->methodToString() == "GET") {
  // correct
  if (!strcmp(request->methodToString(), "GET")) {

La première version donne :
/home/riton/projects/webserver-form/esp32-subscriber/esp32-subscriber.ino: In function 'void handleRoot(AsyncWebServerRequest*)':
esp32-subscriber:44:36: error: comparison with string literal results in unspecified behaviour [-Werror=address]
if (request->methodToString() == "GET") {

Le problème est que je pensais que methodToString() retournait une String, or elle retourne const char *.

Sans activer les warnings on ne voit pas l'erreur.

Merci pour vos réponses.
Je penses effectivement que la fonction renvois la dernière valeur calculée.
C'est bluffant, car l'élève ne comprenait pas son érreur.
Je sais que les érreur grossière de la variable en global au lieu de local n'est pas normal, je lui en ai fait par, ainsi que d'autre problème.
C'est le moment délicat où l'élève ne comprends pas pourquoi c'est pas bon, alors que ça marche.
Je vais éssayer de lui fournir une explication.
Mais c'est quelque chose qui m'avait intrigué, surtout qu'à la compilation l'IDE ARDUINO ne donne pas d'érreur.
Merci à vous.

Je penses effectivement que la fonction renvois la dernière valeur calculée.

Non elle ne renvoie vraiment rien.

il se trouve que côté appelant, croyant qu'il y aura quelque chose de propre sur la pile à lire, on va lire cette valeur puisqu'on demande une affectation. Il lit donc ce qui trainait à cet endroit.

C'est un peu compliqué car il faut comprendre comment un programme en C ou C++ transmet sa valeur de retour à l'appelant.

activez les warning et vous verrez que sur Arduino dans la vraie vie vous serez prévenu

D'accord, je penses que ça va pas être simple à expliquez. Mais il y a des règles à respecter.
Donc je vais leurs expliquez qu'il faut un return.

Merci à vous. De toutes ces lumières.
Il vont pouvoir continué sur de bonne base.

C'est pas évident d'expliquer ça à distance. Vive le virtuel.

Bon courage à vous tous en ces moments difficiles.

C'est un peu compliqué car il faut comprendre comment un programme en C ou C++ transmet sa valeur de retour à l'appelant.

Souvent dans un registre, si c'est un entier.
Mais comme l'AVR a des registres 8bits, il se peut qu'il passe par deux registres.
Il faudrait demander la génération de l'assembleur.

:confused:
Oups désolé, je suis allé trop vite...

GillesT:
D'accord, je penses que ça va pas être simple à expliquez. Mais il y a des règles à respecter.
Donc je vais leurs expliquez qu'il faut un return.

Merci à vous. De toutes ces lumières.
Il vont pouvoir continué sur de bonne base.

vous êtes vraiment le prof ?

hbachetti:
Souvent dans un registre, si c'est un entier.
Mais comme l'AVR a des registres 8bits, il se peut qu'il passe par deux registres.
Il faudrait demander la génération de l'assembleur.

ou sur la pile pour les types un peu plus longs.