web-dev-qa-db-fra.com

Est-il possible d'utiliser une instruction if dans #define?

J'essaie de créer une macro avec la formule suivante: (a^2/(a+b))*b et je veux m'assurer qu'il n'y aura pas de division par zéro.

#define SUM_A( x, y ) if( x == 0 || y == 0) { 0 } else { ( ( ( x * x ) / ( ( x ) + ( y ) ) ) * ( y ) )}

et puis j'appelle la macro à l'intérieur de main:

float a = 40, b = 10, result; 
result = SUM_A(a, b); 
printf("%f", result);

J'ai essayé d'utiliser des crochets autour de la fonction if, mais je continue à avoir des erreurs de syntaxe avant l'instruction if. J'ai également essayé d'utiliser return, mais j'ai lu quelque part que vous n'êtes pas censé l'utiliser pour définir.

11
Frey1337

Vous ne pouvez pas utiliser l'instruction if, car #define est interprété par le préprocesseur et le résultat obtenu

 result=if( x == 0 || y == 0) { 0 } else { ( ( ( x * x ) / ( ( x ) + ( y ) ) ) * ( y ) )}

ce qui est une mauvaise syntaxe.

Mais une alternative consiste à utiliser l'opérateur ternaire. Changez votre définition en

#define SUM_A( x, y )  ((x) == 0 || (y) == 0 ? 0 : ( ( ( (x) * (x) ) / ( ( x ) + ( y ) ) ) * ( y ) ))

N'oubliez pas de toujours mettre votre définition entre parenthèses pour éviter les erreurs de syntaxe lors du remplacement.

19
tomahh

if introduit une déclaration, pas une expression. Utilisez l'opérateur "ternaire" (conditionnel):

#define SUM_A(x, y) (((x) == 0 || (y) == 0)? 0: ((((x) * (x)) / ((x) + (y))) * (y)))

Sinon, faites-en une fonction inline:

inline float sum_a(float x, float y)
{
    if (x == 0 || y == 0)
        return 0;
    else
        return ((x * x) / (x + y)) * y;
}

Cela évite le problème de l'évaluation multiple de x et/ou y et est beaucoup plus lisible, mais il corrige les types de x et y. Vous pouvez également supprimer la variable inline et laisser le compilateur décider si l’intégration de cette fonction en vaut la peine (inline n’est pas une garantie de son exécution inline).

6
Fred Foo

Techniquement, il est possible d'utiliser des instructions if dans un #define (mais pas comme prévu). Puisque #defines ne sont que des substitutions de texte, vous devez faire très attention à la façon dont vous les développez. J'ai trouvé que ça marche ...

#define SUM_A(x, y)                                        \
({                                                         \
    double answer;                                         \
    if ((x) == 0 || (y) == 0)                              \
        answer = 0;                                        \
    else                                                   \
        answer = ((double)((x)*(x)) / ((x)+(y))) * (y);    \
    (answer);                                              \
})
// Typecasting to double necessary, since int/int == int in C

Cela devrait vous donner le résultat que vous recherchez, et rien n’empêche qu’il soit impossible d’inclure également plusieurs else ifs (bien que, comme d’autres réponses l’ont indiqué, il est probablement plus facile d’utiliser l’opérateur ternaire).

3
Saeed Baig

Le problème est qu'une instruction if n'est pas une expression et ne renvoie pas de valeur. En outre, il n'y a aucune bonne raison d'utiliser une macro dans ce cas. En fait, cela pourrait causer de très graves problèmes de performances (selon ce que vous transmettez en tant que macro arguments). Vous devriez utiliser une fonction à la place.

1
Alex D

Votre macro pose plusieurs problèmes:

  • il se développe en une instruction, vous ne pouvez donc pas l'utiliser comme expression

  • les arguments ne sont pas correctement parenthèses dans le développement: appeler cette macro avec autre chose que des noms de variables ou des constantes posera des problèmes.

  • les arguments sont évalués plusieurs fois: si vous appelez la macro avec des arguments ayant des effets secondaires, tels que SUM_A(a(), b()) ou SUM_A(*p++, 2), l'effet secondaire se produira plusieurs fois.

Pour éviter tous ces problèmes, utilisez une fonction, éventuellement définie en tant que static inline, pour aider le compilateur à optimiser davantage (ceci est optionnel et les compilateurs modernes le font automatiquement):

static inline int SUM_A(float x, float y) {
    if (x == 0 || y == 0)
        return 0; 
    else
        return x * x / (x + y) * y;
}

Remarques:

  • cette fonction utilise l'arithmétique en virgule flottante, ce que la macro ne ferait pas nécessairement, en fonction des types réels de ses arguments.
  • le test n'empêche pas la division par zéro: SUM_A (-1, 1) en effectue toujours un.
  • la division par zéro n'est pas nécessairement un problème: avec les arguments à virgule flottante, cela produit un Infinity ou un NaN, pas une erreur d'exécution.
1
chqrlie

J'utilise assez souvent les macros avec conditions et elles ont un usage légitime.

J'ai quelques structures qui sont essentiellement des blobs et tout n'est qu'un flux uint8_t.

Pour rendre les structures internes plus lisibles, j'ai des macros conditionnelles.

Exemple...

#define MAX_NODES 10
#define _CVAL16(x)(((x) <= 127) ? (x) : ((((x) & 127) | 0x80) ), ((x) >> 7))  // 1 or 2 bytes emitted <= 127 = 1 otherwise 2

Maintenant, pour utiliser la macro dans un tableau ...

uint8_t arr_cvals[] = { _CVAL16(MAX_NODES), _CVAL16(345) };

Trois octets sont émis dans le tableau, la 1ère macro émet 1 et les 2 derniers octets. Ceci est évalué à la compilation et rend le code plus lisible.

J'ai aussi ... par exemple ...

#define _VAL16(x) ((x) & 255), (((x) >> 8) & 255)

Pour le problème initial ... peut-être que la personne souhaite utiliser les résultats avec des constantes, mais une fois encore, il s'agit vraiment de savoir où et comment les utiliser.

#define SUM_A(x, y) (!(x) || !(y)) ? 0 : ((x) * (x) / ((x) + (y)) * (y))
float arr_f[] = { SUM_A(0.5f, 0.55f), SUM_A(0.0f, -1.0f), SUM_A(1.0f, 0.0f) };

Au moment de l'exécution peut avoir ...

float x;
float y;

float res = SUM_A(x,y); // note ; on the end

J'ai un programme qui crée des polices qui sont incluses en tant que code dans les programmes C et la plupart des valeurs sont entourées de macros divisant les valeurs 32 bits en 4 octets, flottant en 4 octets, etc.

0
TheWhitde

OUI vous pouvez avoir une instruction if dans une macro. Vous devez le formater correctement. Voici un exemple:

#define MY_FUNCTION( x )  if( x ) { PRINT("TRUE"); } else { PRINT("FALSE"); } 
0
Sarena Meas