ProgrammationDéveloppeur embarqué, Développeur backend

Expliquez le fonctionnement et le fonctionnement de l'opérateur conditionnel (opérateur ternaire) en C. Quelles sont les subtilités liées à la conversion de types et aux effets secondaires qui se produisent lors de son utilisation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

L'opérateur conditionnel ternaire (?:) permet de calculer et de retourner l'une des deux expressions en fonction d'une condition :

result = cond ? expr_true : expr_false;
  • Le type du résultat est déterminé selon les règles générales des "conversions arithmétiques habituelles" (si les deux expressions sont des nombres) ou par le type de l'opérande le plus grand (pour les pointeurs et les structures).
  • Les deux expressions doivent être compatibles en termes de type. Si elles sont de types différents, le compilateur tentera de les convertir en un type commun, parfois avec des résultats inattendus ou une perte de données.
  • L'opérateur ternaire accepte la présence d'effets secondaires dans les deux expressions, mais seul celui requis par la condition est évalué.
  • Dans le cas d'expressions complexes, des problèmes d'ambiguïté ou de comportement imprévisible peuvent survenir (surtout en cas d'imbrication ou d'utilisation avec des macros et des fonctions).

Exemple

int a = 10, b = 0; int max = (a > b) ? a : b; // max = 10

Question piège

"Si deux expressions de l'opérateur ternaire retournent des objets de types différents (par exemple, un pointeur et zéro), quel sera le type du résultat ?"

Beaucoup affirment que le compilateur "devine" toujours le type du résultat. En réalité, si l'une des expressions est un pointeur et l'autre est 0 ou NULL, le résultat aura le type du pointeur. Si la différence est plus complexe — par exemple, un pointeur d'un type différent ou un type int et un type enum — il peut y avoir une perte d'information non évidente, et parfois le compilateur renvoie une erreur.

struct node *p = NULL; void *v = cond ? p : NULL; // ok void *z = cond ? p : 0; // ok int i = cond ? 0 : "abc"; // erreur : types incompatibles

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Dans un grand projet de cross-compilation, l'expression utilisée cond ? ptr : 0 retournait un pointeur avec un compilateur et un int avec un autre (où 0 était interprété strictement comme un int). Le système plantait lors de l'utilisation du résultat comme pointeur.


Histoire

Dans un package financier, où les fonctions de retour utilisaient l'opérateur ternaire avec des littéraux " nus " (cond ? 0.0 : 1), le type du résultat était devenu par inadvertance double, bien que l'on s'attendait à ce que ce soit un int, ce qui a conduit à des erreurs de comparaison et d impression.


Histoire

Dans l'une des bibliothèques, un appel était effectué avec une expression à effet secondaire : flag ? inc(x) : dec(x). Lors du refactoring, une erreur cachée : les deux expressions appelaient une fonction (avec leurs effets secondaires), alors qu'on s'attendait à ce qu'une seule soit exécutée. La confusion avec les macros imbriquées a conduit à une modification double de la valeur, ce qui n'a été découvert qu'au cours de tests détaillés.