为什么static_cast是一个分布式表达的表达式?

时间:2015-01-13 20:44:24

标签: c++ casting static-cast

我需要取2个无符号8位值并减去它们,然后将该值加到32位累加器中。 8位减法可能会下溢,而且没问题(unsigned int underflow是定义的行为,因此没有问题)。

我希望static_cast<uint32_t>(foo - bar)能够做我想做的事情(foobar都是uint8_t)。但看起来这会先抛出它们然后然后执行32位减法,而我需要它作为8位变量下溢。我知道我可以只修改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

我怀疑diff3diff1具有相同的行为,但它实际上与diff2相同。

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

2 个答案:

答案 0 :(得分:8)

对于大多数算术运算符(包括-),操作数经历通常的算术转换。其中一种转换是,任何比int更窄的类型值都会提升为int。 (标准参考:[expr]/10)。

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

要获得您想要的结果,请转到uint8_t而不是uint32_t。或者,对转换结果使用模数运算符% uint32_t

不可能以比int

更窄的精度直接进行计算

答案 1 :(得分:4)

问题不是static_cast而是减法,加法运算符的操作数具有应用于它们的通常算术转换,在这种情况下是整数促销,这导致减法的两个操作数被提升为 INT

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

另一方面:

static_cast<uint8_t>(foo - bar);

会产生预期的结果。

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

  

加法运算符+和 - 从左到右分组。执行通常的算术转换   算术或枚举类型的操作数。

这导致整体促销,5 [expr] 部分说:

  

否则,应在两个操作数上执行整体促销(4.5)

导致两个操作数都转换为 int ,部分4.5 [conv.prom] 表示:

  

除bool,char16_t,char32_t或wchar_t之外的整数类型的整数转换的整数转换   如果int可以表示all,则rank(4.13)小于int的rank可以转换为int类型的prvalue   源类型的值;否则,源prvalue可以转换为unsigned类型的prvalue   中间体

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

  

如果目标类型是无符号的,则结果值是与源一致的最小无符号整数   整数(模2n,其中n是用于表示无符号类型的位数)。 [

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