79 votes

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

J'utilise un SDK pour un projet embarqué. Dans ce code source, j'ai trouvé un code que j'ai trouvé particulier. A de nombreux endroits dans le SDK il y a du code source dans ce format :

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

#define ATCI_IS_UPPER( alpha_char )  ( ( (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-ce pas ?

#define FOO (1 > 0)

la même chose 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 soient égaux. Y a-t-il une raison d'écrire le code comme ils l'ont fait ?

132voto

Bathsheba Points 23209

Vous avez raison, en C, c'est tautologique. Votre conditionnel ternaire particulier et (1 > 0) sont de type int .

Mais il serait Cependant, en C++, dans certains cas curieux (par exemple, en tant que paramètres 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 y a réfléchi, dans le but de préserver la compatibilité C++.

28voto

unwind Points 181987

Il existe des outils de linting qui considèrent que le résultat d'une comparaison est booléen et ne peut pas être utilisé directement dans l'arithmétique.

Je ne veux pas citer de noms ou montrer du doigt, mais PC-lint est un tel outil de linting .

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

19voto

Zack Points 44583

Vous verrez parfois cela dans très vieux du code, avant qu'il n'y ait une norme C pour préciser que (x > y) est évalué à 1 ou 0 ; certains processeurs préfèrent qu'il soit évalué à -1 ou 0 à la place, et certains très vieux Les compilateurs ont peut-être simplement suivi, de sorte que certains programmeurs ont estimé qu'ils avaient besoin de cette défense supplémentaire.

Vous verrez aussi parfois cela parce que similaire expressions Ne le fais pas. doivent nécessairement être évaluées à 1 ou 0.

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

l'intérieur & -L'expression est évaluée à 0 ou à la valeur numérique de l'option F_DO_GRENFELZ qui est probablement pas 1, donc le ? 1 : 0 sert à le canoniser. Personnellement, je pense qu'il est plus clair d'écrire ça en tant que

#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 ces tests, testant différents types d'expressions, quelqu'un aurait pu décider qu'il était plus facile à maintenir de mettre ? 1 : 0 à l'extrémité de tous d'eux que de s'inquiéter de ceux qui en ont réellement besoin.

15voto

Konchog Points 544

Il y a un bug dans le code du SDK, et le ternaire était probablement une astuce pour le corriger.

S'agissant d'une 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 il faut toujours mettre entre parenthèses les arguments de macro dans l'expansion.

Ainsi, dans votre exemple (mais avec des paramètres), ces deux éléments sont bogués.

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

Ils seraient très correctement remplacés par

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

@CiaPan Fait un excellent point dans le commentaire suivant qui est 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 '{'**

5voto

ThorX89 Points 967

En C, cela n'a pas d'importance. Les expressions booléennes en C ont le type int et une valeur qui est soit 0 ou 1 donc

ConditionalExpr ? 1 : 0

n'a aucun effet.

En C++, il s'agit effectivement d'un cast vers int car les expressions conditionnelles en C++ ont le type bool .

#include <stdio.h>
#include <stdbool.h>

#ifndef __cplusplus

#define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") );

#else
template<class T>
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é recherché et que l'auteur ait simplement pensé que cela rendait le code plus clair.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X