24

我正在尝试使用以下公式制作宏:(a^2/(a+b))*b,并且我想确保不会被零除。

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

然后我在main里面调用宏:

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

我尝试在 if 函数周围使用方括号,但在 if 语句之前我不断收到语法错误。我也尝试过使用 return,但我在某处读到你不应该在定义中使用它。

4

8 回答 8

32

您不能使用 if 语句,因为#define它是由预处理器解释的,并且输出将是

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

这是错误的语法。

但另一种方法是使用三元运算符。将您的定义更改为

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

请记住始终将您的定义放在括号之间,以避免替换时出现语法错误。

于 2012-10-20T14:19:37.960 回答
16

据我所知,您尝试做的事情(使用if语句,然后从宏中返回一个值)在 ISO C 中是不可能的......但是使用语句表达式(GNU 扩展)在某种程度上是可能的。

由于#defines 本质上只是花哨的文本查找和替换,因此您必须非常小心它们的扩展方式。我发现这在默认情况下gcc有效:clang

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

这个宏里面的东西的简单解释:

  • 每行的\末尾是信号行继续(即告诉编译器“这个宏在下一行继续”)
  • ({是一个语句表达式(GNU 扩展;不是标准 C 的一部分)。
  • 虽然不是绝对必要的,但将参数的每次使用都放在括号中会更安全,以避免运算符优先级陷阱。例如,如果x2+1,那么(x)*(x)将扩展为(2+1)*(2+1),即 9(我们想要的),但x*x会扩展为2+1*2+1,即 5(不是我们想要的)
  • 在语句表达式中,最后一行的功能类似于return值(因此answer;在末尾)

这应该会给你你正在寻找的结果,并且没有理由不能将它扩展到包含多个else ifs (尽管正如其他答案所指出的那样,如果可以的话,最好使用三元运算符)。

于 2017-11-19T08:33:12.887 回答
6

if引入语句,而不是表达式。使用“三元”(条件)运算符:

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

或者,将其设为inline函数:

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

x这避免了和/或的多重评估问题,并且y更具可读性,但它确实修复了xand的类型y。您也可以删除inline并让编译器决定是否内联此函数(inline不能保证它会执行内联)。

于 2012-10-20T14:19:35.907 回答
3

您的宏存在多个问题:

  • 它扩展为语句,因此您不能将其用作表达式

  • 扩展中的参数没有正确括起来:使用除变量名或常量之外的任何内容调用此宏都会产生问题。

  • 参数被多次评估:如果您使用具有副作用的参数调用宏,例如SUM_A(a(), b())or SUM_A(*p++, 2),则副作用将多次发生。

为了避免所有这些问题,请使用一个函数,可能定义为static inline帮助编译器进行更多优化(这是可选的,现代编译器会自动执行此操作):

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

笔记:

  • 此函数使用浮点运算,宏不一定会使用,具体取决于其参数的实际类型。
  • 该测试不会阻止除以零:SUM_A(-1, 1) 仍然执行一。
  • 除以零不一定是问题:使用浮点参数,它会产生 Infinity 或 NaN,而不是运行时错误。
于 2017-06-25T10:20:44.157 回答
1

问题是if语句不是表达式,也不返回值。此外,在这种情况下没有充分的理由使用宏。事实上,它可能会导致非常严重的性能问题(取决于您作为宏参数传递的内容)。您应该改用函数。

于 2012-10-20T14:20:29.643 回答
0

是的,您可以在宏中使用 if 语句。您需要正确格式化它。这是一个例子:

#define MY_FUNCTION( x )  if( x ) { PRINT("TRUE"); } else { PRINT("FALSE"); } 
于 2018-03-08T19:05:43.950 回答
0

我经常使用带有条件的宏,它们确实有合法用途。

我有一些本质上是 blob 的结构,一切都只是一个 uint8_t 流。

为了使内部结构更具可读性,我有条件宏。

例子...

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

现在要在数组中使用宏...

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

数组中发出三个字节,第一个宏发出 1,第二个宏发出 2 个字节。这是在编译时评估的,只是使代码更具可读性。

我也有……例如……

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

对于最初的问题......也许这个人想要使用带有常量的结果,但再次真正归结为在哪里以及如何使用它。

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

在运行时可以有...

float x;
float y;

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

我有一个程序可以创建作为代码包含在 C 程序中的字体,并且大多数值都包含在将 32 位值拆分为 4 个字节、浮动为 4 个字节等的宏周围。

于 2019-02-24T16:11:07.143 回答
0

您可以将条件语句转换为简单的表达式。条件评估为01

// pseudo-code
// if (<something>) { 0; } else { 42; }
// if (!<something>) { 42; } else { 0; }
// !<something> * 42;

在您的具体情况下

// if ((x == 0) || (y == 0)) { 0; } else { (x)(y)expression; }
// if ((x != 0) && (y != 0)) { (x)(y)expression; }
// ((x != 0) && (y != 0)) * ( (x)(y)expression );
于 2020-04-23T09:55:58.053 回答