C定义就地异或问题解释
给出了这个代码 -
#define DO_SOMETHING(a, b) a^=b^=a^=b
我需要选择有关代码功能的选项之一 -
a) 将参数 SET 中的所有位
b) 计算两个参数 DIFF 之间的距离
c) 在两个参数之间替换 SWAP
d) 他们是代码中的错误
e) 在两个参数之间乘以 MULT
建议答案为c。但我不明白怎么做。谁能解释一下?
编辑 :
我尝试运行代码,得到以下输出 -
main.c: In function ‘main’:
main.c:3:25: error: lvalue required as left operand of assignment
3 | #define D_(a, b) a^=b^=a^=b
| ^~
main.c:7:20: note: in expansion of macro ‘D_’
7 | printf("%dn", D_(1, 2));
| ^~
回答
这个问题解释了按位异或运算如何交换值。但是,由于 C 2018 6.5 2 中的这条规则,此处显示的代码的行为并未由 C 标准定义:
如果相对于对同一标量对象的不同副作用或使用同一标量对象的值进行的值计算,标量对象的副作用是未排序的,则行为未定义。
在 中a^=b^=a^=b,最右边a^=b是复合赋值。它计算左右操作数(a和b),对它们进行异或运算,并产生将在表达式中进一步使用的值a。作为副作用,它将该值存储在a.
然后在 中b^=a^=b,这个新^=的取值b和a^=b(如上所述),对它们进行异或,并产生它将存储在 中的值b。作为副作用,它将该值存储在b.
最后,在 中a^=b^=a^=b,新^=的值a与上面产生的值进行异或运算。作为副作用,它将该值存储在a.
现在我们有两个副作用可以修改a. 副作用是与表达式的主要评估分开完成的事情。上面引用的规则意味着多个副作用不应修改同一个对象;有些东西应该将它们分开。
分离副作用的一个简单方法是将赋值放在单独的语句中,因为每个表达式语句后面都有一个序列点:
a ^= b;
b ^= a;
a ^= b;
除了关于表达式语句的序列点之外,C 还有其他关于序列的规则。例如,赋值语句的一个规则是更新左操作数的副作用在操作数的值计算之后进行排序。所以我们知道,在a^=b^=a^=bstore 中a计算出 的值后会发生b^=a^=b。然而,副作用 ina^=b不是值计算的一部分,所以我们不知道副作用 ina^=b^=a^=b是在副作用 in 之后排序的a^=b。学习这些规则可能需要一段时间,因为其中一些规则很棘手。在大多数情况下,程序员应该通过限制在表达式中使用带有副作用的操作来避免它们。
补充说明
此外,我意识到其他人编写了您要询问的宏,但您应该知道将宏参数括在宏替换文本中的括号中是一种很好的做法:
#define DO_SOMETHING(a, b) (a)^=(b)^=(a)^=(b)
这是因为宏可以使用不仅仅是简单名称的参数来调用,例如使用其他运算符的表达式,并且这些可能导致形成与宏作者预期的结构不同的表达式。(我认为这可能不会发生在这个特定的宏中,因为赋值是除逗号运算符之外的最低优先级运算符,并且逗号运算符不能在宏参数中不受保护地出现,因为它将被视为参数分隔符。但是,使用括号是尽管如此,还是很好的做法。)