我尝试了以下代码作为实现交换的天真的尝试R
and B
中的字节数ABGR
word
#include <stdio.h>
#include <stdint.h>
uint32_t ABGR_to_ARGB(uint32_t abgr)
{
return ((abgr ^= (abgr >> 16) & 0xFF) ^= (abgr & 0xFF) << 16) ^= (abgr >> 16) & 0xFF;
}
int main()
{
uint32_t tmp = 0x11223344;
printf("%x %x\n", tmp, ABGR_to_ARGB(tmp));
}
令我惊讶的是,这段代码在 C++17 模式下的 GCC 中“有效”——字节被交换
http://coliru.stacked-crooked.com/a/43d0fc47f5539746 http://coliru.stacked-crooked.com/a/43d0fc47f5539746
但它不应该交换字节! C++17 明确指出,赋值的 RHS 应该在 LHS 之前[完全]排序,这也适用于复合赋值。这意味着在上面的表达式中,每个 RHS^=
应该使用原始值abgr
。因此最终的结果是abgr
应该简单地有B
字节异或R
字节。这就是 Clang 产生的结果(有趣的是,带有排序警告)
http://coliru.stacked-crooked.com/a/eb9bdc8ced1b5f13 http://coliru.stacked-crooked.com/a/eb9bdc8ced1b5f13
快速浏览 GCC 汇编
https://godbolt.org/g/1hsW5a https://godbolt.org/g/1hsW5a
揭示它似乎按倒序排列:LHS 在 RHS 之前。这是一个错误吗?或者这是海湾合作委员会有意识的决定?或者我误解了什么?
表现出完全相同的行为int a = 1; (a += a) += a;
,GCC 计算出a == 4
然后叮叮当当a == 3
.
潜在的歧义源于标准的这一部分(来自工作草案 N4762):
[expr.ass]:7.6.18 赋值和复合赋值运算符
第 1 段:赋值运算符 (=) 和复合赋值运算符都是从右到左分组的。都需要一个
可修改的左值作为其左操作数;它们的结果是引用左操作数的左值。结果全部
如果左操作数是位域,则 case 是位域。在所有情况下,赋值都在值之后排序
右操作数和左操作数的计算,以及赋值表达式的值计算之前。
右操作数在左操作数之前排序。对于不确定顺序
函数调用时,复合赋值操作是单次求值。
第 7 段:E1 op = E2 形式的表达式的行为等同于 E1 = E1 op E2,只不过 E1 是
仅评价一次。在 += 和 -= 中,E1 要么具有算术类型,要么是指向可能的
cv 限定的完全定义的对象类型。在所有其他情况下,E1 应具有算术类型。
GCC似乎正在利用这个规则进行内部改造(a += a) += a
to (a = a + a) += a
to a = (a = a + a) + a
(since a = a + a
必须仅评估一次) - 并且对于此表达式,正确应用了排序规则。
然而,Clang 似乎以不同的方式执行最后一个转换步骤:auto temp = a + a; temp = temp + a; a = temp;
不过,两个编译器都会对此发出警告(来自原始代码):
因此,编译器编写者知道这种歧义,并决定以不同的方式确定优先级(GCC:第 7 段 > 第 1 段;clang:第 1 段 > 第 7 段)。
这似乎是标准中的一个缺陷。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)