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

J’essaie de faire 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 aussi essayé d’utiliser return, mais j’ai lu quelque part que vous n’êtes pas censé l’utiliser pour définir.

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

  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.

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 en 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 cela corrige les types de x et y . Vous pouvez également supprimer l’ 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).

Techniquement, il est possible d’utiliser des instructions if dans un #define (mais pas de la manière attendue). Puisque les #define sont que des substitutions de texte, vous devez être très prudent quant à la manière de les développer. 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’en étendre la multiplication par d’ else if (si, comme d’autres réponses l’ont indiqué, il est probablement plus facile d’utiliser l’opérateur ternaire).

Le problème est qu’une instruction if n’est pas une expression et ne renvoie pas de valeur. En outre, il n’ya 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.

Votre macro pose plusieurs problèmes:

  • il se développe en une déclaration, 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 comme 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.

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"); }