Pourquoi l’opérateur ternaire est-il utilisé pour définir 1 et 0 dans une macro?

J’utilise un SDK pour un projet incorporé. Dans ce code source, j’ai trouvé du code que j’ai au moins trouvé particulier. Dans de nombreux endroits du SDK, il existe un code source dans ce format:

#define ATCI_IS_LOWER( alpha_char ) ( ( (alpha_char >= ATCI_char_a) && (alpha_char = ATCI_CHAR_A) && (alpha_char <= ATCI_CHAR_Z) ) ? 1 : 0 ) 

L’utilisation de l’opérateur ternaire ici fait-elle une différence?

N’est pas

 #define FOO (1 > 0) 

le même que

 #define BAR ( (1 > 0) ? 1 : 0) 

?

J’ai essayé de l’évaluer en utilisant

 printf("%d", FOO == BAR); 

et obtenir le résultat 1, il semble donc qu’ils sont égaux. Y a-t-il une raison d’écrire le code comme ils l’ont fait?

Vous avez raison, en C c’est tautologique. Votre condition ternaire particulière et (1 > 0) sont de type int .

Mais cela importerait en C ++, dans certains cas curieux (par exemple, en tant que parameters de fonctions surchargées), puisque votre expression conditionnelle ternaire est de type int , alors que (1 > 0) est de type bool .

Je pense que l’auteur a réfléchi à cela, en vue de préserver la compatibilité C ++.

Certains outils de peluches sont d’avis que le résultat d’une comparaison est booléen et ne peut être utilisé directement en arithmétique.

Ne pas nommer de noms ni pointer de doigts, mais PC-lint est un tel outil .

Je ne dis pas qu’ils ont raison, mais c’est une explication possible à la raison pour laquelle le code a été écrit comme ça.

Vous verrez parfois cela dans un code très ancien , car auparavant il y avait un standard C pour préciser que (x > y) évalué à 1 ou 0 numérique; certains processeurs préféreraient que cela soit évalué à −1 ou 0, et certains compilateurs très anciens viennent juste de suivre, alors certains programmeurs ont estimé qu’ils avaient besoin d’un système de défense supplémentaire.

Vous verrez parfois cela aussi car les expressions similaires ne sont pas nécessairement numériques 1 ou 0. Par exemple, dans

 #define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0) 

l’ F_DO_GRENFELZ interne & -expression est évaluée à 0 ou à la valeur numérique de F_DO_GRENFELZ , qui n’est probablement pas 1, alors le ? 1 : 0 ? 1 : 0 sert à la canoniser. Personnellement, je pense qu’il est plus clair d’écrire cela comme

 #define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0) 

mais les gens raisonnables peuvent être en désaccord. Si vous aviez tout un tas de choses à la suite, en testant différents types d’expressions, quelqu’un aurait pu décider que c’était plus facile à mettre en place ? 1 : 0 ? 1 : 0 à la fin de chacun d’eux plutôt que de s’inquiéter de ceux qui en avaient réellement besoin.

Il y a un bogue dans le code SDK, et le ternaire était probablement un kludge pour le réparer.

En tant que macro, les arguments (alpha_char) peuvent être n’importe quelle expression et doivent être mis entre parenthèses, car des expressions telles que «A» && «c» échoueront au test.

 #define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ? 1 : 0 ) std::cout << IS_LOWER('A' && 'c'); **1** std::cout << IS_LOWER('c' && 'A'); **0** 

C'est pourquoi on devrait toujours faire entre parenthèses les arguments de macro dans l'expansion.

Donc, dans votre exemple (mais avec des parameters), ceux-ci sont tous deux buggés.

 #define FOO(x) (x > 0) #define BAR(x) ((x > 0) ? 1 : 0) 

Ils seraient plus correctement remplacés par

 #define BIM(x) ((x) > 0) 

@CiaPan Fait un grand commentaire dans le commentaire suivant, à savoir que l'utilisation d'un paramètre plus d'une fois conduit à des résultats indéfinissables. Par exemple

 #define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z')) char ch = 'y'; std::cout << IS_LOWER(ch++); **1** **BUT ch is now '{'** 

En C, peu importe. Les expressions booléennes en C ont le type int et une valeur égale à 0 ou 1 , donc

 ConditionalExpr ? 1 : 0 

n’a aucun effet.

En C ++, il s’agit effectivement d’une conversion en int , car les expressions conditionnelles en C ++ ont le type bool .

 #include  #include  #ifndef __cplusplus #define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") ); #else template int print_type(T const& x); template<> int print_type<>(int const& x) { return puts("int"); } template<> int print_type<>(bool const& x) { return puts("bool"); } #endif int main() { print_type(1); print_type(1 > 0); print_type(1 > 0 ? 1 : 0); /*c++ output: int int int cc output: int bool int */ } 

Il est également possible qu’aucun effet n’ait été voulu et l’auteur a simplement pensé que cela rendait le code plus clair.

Une explication simple est que certaines personnes ne comprennent pas non plus qu’une condition renverrait la même valeur en C ou qu’elles pensent qu’il est plus propre d’écrire ((a>b)?1:0) .

Cela explique pourquoi certains utilisent également des constructions similaires dans des langages avec des booléens corrects, ce qui dans la syntaxe C serait (a>b)?true:false) .

Cela explique également pourquoi vous ne devriez pas changer cette macro inutilement.

Peut-être, étant un logiciel intégré, donnerait des indices. Peut-être y a-t-il beaucoup de macros écrites en utilisant ce style, pour vous suggérer facilement que les lignes ACTI utilisent une logique directe plutôt qu’une logique inversée.