Comment la priorité des opérateurs C brise votre code (et comment y remédier)

10

Vous écrivez ce que vous voulez dire. Le compilateur lit ce qu’il veut.

Si vous avez déjà regardé une ligne de code C, convaincu qu’elle faisait une chose, pour ensuite la voir faire autre chose, vous avez satisfait à la priorité des opérateurs. Ce n’est pas un bug. C’est une fonctionnalité du langage qui mord même les développeurs chevronnés.

Prenons cette simple arithmétique :

x=5+3*6;

Le cours de mathématiques dit d’ajouter en premier. C dit non. La multiplication et la division se situent à un niveau supérieur à l’addition et à la soustraction. Le compilateur multiplie d’abord 3 par 6. Ensuite, il ajoute 5. X obtient 23. Pas 48. Vous venez de perdre des points dans un quiz. Ou pire, un déploiement en production.

La confusion s’approfondit lorsque vous mélangez les opérateurs avec la syntaxe.

Regardez cette déclaration :

char *a[10];

« a » est-il un pointeur vers un tableau de 10 caractères ? Ou s’agit-il d’un tableau de 10 pointeurs vers des caractères ?

Sans connaître les règles de préséance spécifiques, vous devinez. En C, les crochets [] se lient plus étroitement que les pointeurs *. Il s’agit donc d’un tableau de 10 pointeurs vers des caractères.

C’est important car C n’analyse pas comme l’anglais. Il s’analyse comme une hiérarchie rigide.

Vous pouvez écrire du code qui semble correct mais qui échoue silencieusement en raison de la priorité.

Considérez l’arithmétique des pointeurs mélangée à l’accès à la structure.

*p.i = 10;

Cela ne fonctionne pas comme prévu. L’opérateur point . a une priorité plus élevée que l’opérateur de déréférencement *. Le compilateur essaie d’accéder au membre « i » de l’objet pointé par « p » avant de déréférencer « p ». Mais « p » est un pointeur, pas une structure. Le compilateur s’étouffe. Ou pire, il interprète la mémoire d’une manière qui corrompt les données.

Il faut forcer le problème.

(*p).je = 10;

Les parenthèses gagnent. Toujours.

La hiérarchie des opérateurs

Kernighan et Ritchie, les architectes de C, ont exposé la priorité dans leur texte classique. Le tableau ci-dessous montre la commande. La ligne supérieure est la plus haute. Le bas est le plus bas.

  1. Parenthèses () et Indices []
  2. De gauche à droite.
  3. Opérateurs unaires ++, --, +, -, !, ~, (type), * (déréférence), & (adresse), sizeof
  4. De droite à gauche.
  5. Multiplicatif *, /, %
  6. De gauche à droite.
  7. Additif +, -
  8. De gauche à droite.
  9. Maj <<, >>
  10. De gauche à droite.
  11. Relationnel <, <=, >, >=
  12. De gauche à droite.
  13. Égalité ==, !=
  14. De gauche à droite.
  15. Au niveau du bit ET &
  16. De gauche à droite.
  17. XOR au niveau du bit ^