web-dev-qa-db-fra.com

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

J'utilise un SDK pour un projet intégré. Dans ce code source, j'ai trouvé du code qui au moins m'a semblé étrange. Dans de nombreux endroits du SDK, le code source est 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 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?

79
Viktor S

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

Mais il est vrai que serait importe dans 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.

À mon avis, l'auteur a réfléchi à la question, dans le souci de préserver la compatibilité C++.

132
Bathsheba

Certains outils suggèrent que le résultat d'une comparaison est booléen et ne peut pas être utilisé directement en calcul.

Ne pas nommer les noms ni pointer du doigt, mais PC-lint est un outil aussi utile .

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

29
unwind

Vous verrez parfois ceci dans very old code, alors qu’il existait un standard C spécifiant que (x > y) est évalué à numérique 1 ou 0; Certains processeurs préfèreraient plutôt évaluer -1 ou 0, et certains très vieux compilateurs ont peut-être suivi, donc certains programmeurs ont estimé qu'ils avaient besoin de plus de défense.

Vous verrez parfois aussi cela parce que similaire expressions ne pas doit nécessairement correspondre à 1 ou 0 numérique. Par exemple, dans

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

l'expression &- intérieure est évaluée à 0 ou à la valeur numérique de F_DO_GRENFELZ, ce qui correspond probablement à pas 1, de sorte que ? 1 : 0 sert à le canoniser. Personnellement, je pense qu'il est plus clair d'écrire cela comme

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

mais des personnes raisonnables peuvent être en désaccord. Si vous en avez testé plusieurs types d'affilée, quelqu'un aurait peut-être décidé qu'il était plus facile de maintenir ? 1 : 0 à la fin de tous / que de s'inquiéter des expressions réellement nécessaires. il.

20
zwol

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

Étant 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 macro-arguments du développement.

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

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

Ils seraient le plus correctement remplacés par

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

@CiaPan Fait un bon point dans le commentaire suivant, à savoir que l'utilisation d'un paramètre plusieurs 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 '{'**
16
Konchog

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

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

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

5
Hans Olsson

Peu importe . En C, les expressions booléennes ont le type int et une valeur soit 0, soit 1, donc

ConditionalExpr ? 1 : 0

n'a aucun effet.

En C++, il s'agit effectivement d'un transtypage en 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é prévu, et l'auteur a simplement pensé que cela clarifiait le code.

5
PSkocik

Peut-être, étant un logiciel embarqué, donnerait-il des indices. Peut-être y a-t-il beaucoup de macros écrites en utilisant ce style, pour indiquer que les lignes ACTI utilisent la logique directe plutôt que la logique inversée. 

0
J.Guarin