Evaluation des opérateurs && et || (ET et OU)

Bonjour,

je n'ai pas réussi à trouver d'information sur l'évaluation des opérateurs && et || en C "Arduino", alors je vous livre le résultat de mes investigations.

le problème est de savoir ce que fait le scketch quand il contient une condition du genre :

condition = (condition1) && (condition2);

d'après mes propres expériences, différents compilateurs produisent différents résultats suivant que l'opérateur && est évalué de gauche à droite ou de droite à gauche et si l'évaluation de la deuxième condition (dans l'ordre d'évaluation) dépend du résultat de l'évaluation de la première condition.

Je m'explique par un exemple. Ainsi, pour l'instruction ci-dessus, le programme (une fois compilé) peut évaluer d'abord (condition2) - évaluation de droite à gauche - ou (condition1) - évaluation de gauche à droite. Et si la première condition évaluée est false, il peut évaluer la deuxième (bien que ça ne serve à rien puisque les résultat global sera toujours false), ou bien ne pas le faire, de sorte qu'on peut s'attendre dans des cas extrêmes à plusieurs résultats différents.

ainsi

int i = 2;
int j = 3;
boolean result = (++i < 2) && (++j < 3)

peut conduire à 3 résultats différents suivant l'évaluation du &&. le C "Arduino" conduit à :

result = false
i = 3
j = 3

car l'opérateur && est évalué de gauche à droite, et la deuxième condition (++j<3) n'est pas évaluée, puisque la première est fausse. Quand on dit pas évaluée c'est pas du tout évaluée MEME si il y a un opérateur de pré-incrémentation comme ici ++j. j vaudra donc toujours 3 après calcul de result.

ceci permet des instructions très compactes comme dans cet exemple tiré d'openclassroom où il s’agissait de reproduire avec des LED la bande lumineuse de la voiture K2000. On peut faire la loop en une seule instruction. la fonction allumePinActive() est TOUJOURS appelée (et retourne toujours true mais l'incrémentation (ou la décrémentation) de la pinActive n'est effectuée que si c'est dans le "bon" sens d'allumage. J'ai rajouté des parenthèses inutiles pour la bonne lisibilité du code.

const int NOMBREPIN = 5; 
const int DELAI = 100; 
const int PREMIEREPIN = 2; 

int pinActive = 0; 
boolean gaucheDroite = true;

void setup() {
  for (int i=0; i<NOMBREPIN; i++) {
    pinMode(PREMIEREPIN+i,OUTPUT);
    digitalWrite(PREMIEREPIN+i,LOW); // on ne sait jamais 
  }
}

void loop() {
  if (allumePinActive()&&(((gaucheDroite)&&(++pinActive == NOMBREPIN-1)) || ((!gaucheDroite)&&(!(--pinActive))))) gaucheDroite = !gaucheDroite;
}

boolean allumePinActive(){
  for (int i=0; i<NOMBREPIN; i++) digitalWrite((PREMIEREPIN+i),(i != pinActive));
  delay(DELAI);
  return true;
}

pchaumont:
d'après mes propres expériences, différents compilateurs produisent différents résultats suivant que l'opérateur && est évalué de gauche à droite ou de droite à gauche ...

Bonjour,

Non, la norme C/C++ définit parfaitement la priorité des opérateurs et l'ordre des opérations.

boolean result = (++i < 2) && (++j < 3)

Un exemple parfait de ce qu'il ne faut pas faire.

c'est un exemple parfait de ce qu'il ne faudrait pas faire... justement parce que la norme C/C++ ne définit pas "parfaitement" la priorité des opérateurs :slight_smile:

pchaumont:
je n'ai pas réussi à trouver d'information sur l'évaluation des opérateurs && et || en C "Arduino", alors je vous livre le résultat de mes investigations.
le problème est de savoir ce que fait le scketch quand il contient une condition du genre :
condition = (condition1) && (condition2);

faut lire la doc et les normes... Elle définit parfaitement" la priorité des opérateurs mais l'ordre des évaluations à priorité équivalente n'est pas toujours spécifié dans la norme et dans ce cas cet ordre n'est pas défini et c'est le compilateur qui a le choix.

Order of evaluation of the operands of almost all C++ operators (including the order of evaluation of function arguments in a function-call expression and the order of evaluation of the subexpressions within any expression) is unspecified. The compiler can evaluate operands in any order, and may choose another order when the same expression is evaluated again.

pchaumont:
c'est un exemple parfait de ce qu'il ne faudrait pas faire... justement parce que la norme C/C++ ne définit pas "parfaitement" la priorité des opérateurs :slight_smile:

Si si Pour votre exemple

boolean result = (++i < 2) && (++j < 3)

la norme est aussi très précise la dessus et le résultat est certain.

For the built-in logical AND operator, the result is true if both operands are true. Otherwise, the result is false. This operator is short-circuiting: if the first operand is false, the second operand is not evaluated

  1. The value computation of the built-in post-increment and post-decrement operators is sequenced before its side-effect.
  2. The side effect of the built-in pre-increment and pre-decrement operators is sequenced before its value computation (implicit rule due to definition as compound assignment)
  3. Every value computation and side effect of the first (left) argument of the built-in logical AND operator && and the built-in logical OR operator || is sequenced before every value computation and side effect of the second (right) argument.

boolean result = (++i < 2) && (++j < 3)

Un exemple parfait de ce qu'il ne faut pas faire.

Je suis entièrement d'accord.

Par exercice de style on peut très bien écrire un code hyper compact et jouer avec les limites de l'interprétation du compilateur.

Par expérience, c'est le genre de chose que je ne fais jamais.
Je préfère éliminer toute ambiguïté et ne laisser aucune liberté au compilateur.
Quitte à ajouter des lignes de code et évaluer chaque expression de façon claire, voire à adapter la morphologie du code en rajoutant quelques tests supplémentaires

-1- Dans le cas d'un développement en équipe, si chacun code avec ce genre d'astuce, les collègues vont passer des heures inutiles a essayer de comprendre comment ça devrait fonctionner.
-2- Le calcul des valeurs intermédiaires permet aussi de vérifier chaque valeur indépendemment, via le moniteur série dans notre cas. Ce qui facilite nettement le debuggage.
-3- Imagine que ton code soit peu ou pas documenté et que tu doive y revenir quelques mois ou années après. Je te souhaite bon courage
-4- Pire encore, si l'équipe qui nous fournit un Ide gratuit décide de changer de compilateur. Tu recompiles et tu n'es pas sur que ça fonctionne encore

tout à fait d'accord, la lisibilité et le respect des types sont deux points importants que tout "programmeur" doit respecter.

surtout que la phase d'optimisation à la compilation maintenant va se charger de regrouper ce qui peut l'être pour vous...