为什么 static_cast 在表达式上是分配性的?

Why is static_cast on an expression acting distributively?

我需要取 2 个无符号的 8 位值并将它们相减,然后将此值添加到 32 位累加器。 8位减法可能会下溢,没关系(unsigned int下溢是定义的行为,所以没有问题)。

我希望 static_cast<uint32_t>(foo - bar) 应该做我想做的(其中 foobar 都是 uint8_t)。但看起来这会先转换它们,然后 然后 执行 32 位减法,而我需要它作为 8 位变量下溢。我知道我可以 mod 256,但我想弄清楚 为什么 它以这种方式工作。

此处示例:https://ideone.com/TwOmTO

uint8_t foo = 5;
uint8_t bar = 250;

uint8_t diff8bit = foo - bar;
uint32_t diff1 = static_cast<uint32_t>(diff8bit);

uint32_t diff2 = static_cast<uint32_t>(foo) - static_cast<uint32_t>(bar);

uint32_t diff3 = static_cast<uint32_t>(foo - bar);

printf("diff1 = %u\n", diff1);
printf("diff2 = %u\n", diff2);
printf("diff3 = %u\n", diff3);

输出:

diff1 = 11
diff2 = 4294967051
diff3 = 4294967051

我怀疑 diff3 的行为与 diff1 相同,但实际上与 diff2.

相同

那么为什么会这样呢?据我所知,编译器应该减去两个 8 位值,然后转换为 32 位值,但事实显然不是这样。这与 static_cast 如何在表达式上表现的规范有关吗?

问题不是 static_cast 而是减法,加法运算符的操作数应用了通常的算术转换,在这种情况下,整数提升导致减法的两个操作数都被提升为整数:

static_cast<uint32_t>(foo - bar);
                      ^^^   ^^^

另一方面:

static_cast<uint8_t>(foo - bar);

会产生预期的结果。

来自 C++ 标准草案部分 5.7 [expr.add] 说:

The additive operators + and - group left-to-right. The usual arithmetic conversions are performed for operands of arithmetic or enumeration type.

这导致积分提升,第 5 [expr] 部分说:

Otherwise, the integral promotions (4.5) shall be performed on both operands

这导致两个操作数都被转换为 int,第 4.5 [conv.prom] 部分说:

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int.

,然后应用 static_cast 到 uint32_t,这会导致转换,定义如下 4.7 [conv.integral]:

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [

问题 Why must a short be converted to an int before arithmetic operations in C and C++? 解释了为什么小于 int 的类型被提升为算术运算。

对于大多数算术运算符(包括-),操作数会进行通常的算术转换。这些转换之一是将任何类型窄于 int 的值提升为 int。 (标准参考:[expr]/10)。

所以表达式 foo - bar 变成 (int)foo - (int)bar 给出 (int)-245。然后你将它转换为 uint32_t 这将给出一个大的正数。

要获得您想要的结果,请转换为 uint8_t 而不是 uint32_t。或者,对转换为 uint32_t.

的结果使用取模运算符 %

无法以比 int

更窄的精度直接进行计算