我使用这个#define
宏来计算数组的索引
#define index(x, y) (((x) % 5) + ((y) % 5) * 5)
如果我删除(x)和(y)的大括号
#define index(x, y) ((x % 5) + (y % 5) * 5)
结果不同。
有人可以解释一下这种行为吗?我这样用:
uint64_t a[25];
...
unsigned int x,y;
uint64_t C[5];
for (y = 0; y < 5; y++) {
for (x = 0; x < 5; x++) {
C[x] = a[index(x, y)] ^ ((~a[index(x + 1, y)]) & a[index(x + 2, y)]);
}
for (x = 0; x < 5; x++ ) {
a[index(x, y)] = C[x];
}
}
答案 0 :(得分:3)
根据第一个定义,这个:
index(x + 1, y)
扩展为:
(((x + 1) % 5) + ((y) % 5) * 5)
第二个,它扩展为:
((x + 1 % 5) + (y % 5) * 5)
问题是x + 1 % 5
。由于%
的优先级高于+
,因此它等同于x + (1 % 5)
,这不是您想要的。
扩展宏时,预处理器会扩展令牌序列。它没有注意运算符优先级。
类似的例子:https://github.com/Keith-S-Thompson/42
#include <stdio.h>
#define SIX 1+5
#define NINE 8+1
int main(void)
{
printf("%d * %d = %d\n", SIX, NINE, SIX * NINE);
return 0;
}
答案 1 :(得分:1)
因为宏参数是有效的复制粘贴代码:
鉴于此:
#define index(x, y) ((x % 5) + (y % 5) * 5)
然后这就是转型
a[index(x + 1, y)]
a[((x + 1 % 5) + (y % 5) * 5)]
请注意第一部分:
(x + 1 % 5)
这正是为什么每本教学书都应该提到你应该在括号中包装宏参数。
答案 2 :(得分:0)
因为当你使用:
#define index(x, y) ((x % 5) + (y % 5) * 5)
index(x + 1, y)
被视为index((x + 1 % 5) + (y % 5) *5)
由于modulo(%
)的优先级高于+
,因此结果错误。
查看优先级表here。
答案 3 :(得分:0)
始终在宏参数周围添加括号。
宏扩展在编译之前发生,它只是替换参数的文本,因为它们在代码中。在预编译时,没有要解释的值,因此必须使用括号来保持操作的顺序。
在您的示例中,宏index(x + 1, y)
已扩展为:
((x + 1 % 5) + (y % 5) * 5)
由于模运算符优先于加法,1 % 5
在加到x
之前计算结果为1。
答案 4 :(得分:0)
嗯,你不必,但它可能会导致奇怪的情况。 #defines会直接进行文本替换,所以想象一下这种情况:
#define foo(x) (x << 1)
...
int r = 1;
int y = foo( r + 1 );
y的价值是多少?
您最想要的是y = 4
。这是R加1,然后向左移1
但实际上你得到y = 3
。为什么?直接替换和操作顺序优先。
预处理器替换如下:
y = r + 1 << 1;
由于班次具有更高的预测性,因此它会在添加之前发生。
现在,将此应用于您的示例,在此行中:
C[x] = a[index(x, y)] ^ ((~a[index(x + 1, y)]) & a[index(x + 2, y)]);
^^^^^^ ^^^^^
这些部分引起了你的不同,当替换发生时没有括号你得到第一部分:
index(x + 1, y)] ..... (x + 1 % 5) + (y % 5) * 5)
^^^^^^^^^
看到问题? 1%5 = 1因此x总是加1 而括号:
index(x + 1, y)] ..... ((x + 1) % 5) + ((y) % 5) * 5)
^^^^^^^^^
你会得到你期望的行为x + 1,然后是mod 5.