uint32_t a = 0xFF << 8;
uint32_t b = 0xFF;
uint32_t c = b << 8;
我正在为Uno(1.0.x和1.5)进行编译,a
和c
显然应该是相同的值,但它们不是......至少不是在目标上运行时。我在主机上编译相同的代码,没有任何问题。
右移工作正常,左移仅在我将变量与常数移动时起作用。
有人可以证实吗?
我正在使用Visual Micro和VS2013。使用1.0.x或1.5 Arduino进行编译会导致同样的失败。
目标:
A = 0xFFFFFF00
C = 0x0000FF00
答案 0 :(得分:1)
问题与签名/无符号隐式演员有关。
使用uint32_t a = 0xFF << 8;
表示
0xFF
;它是signed char
; 0xFFFFFFFF
; a = 0xFFFFFF00
。注意:这有点不对,请参阅下面的&#34;更正确&#34;版本
如果要重现相同的行为,请尝试以下代码:
uint32_t a = 0xFF << 8;
uint32_t b = (signed char)0xFF;
uint32_t c = b << 8;
Serial.println(a, HEX);
Serial.println(b, HEX);
Serial.println(c, HEX);
结果是
FFFFFF00
FFFFFFFF
FFFFFF00
或者,另一方面,如果你写
uint32_t a = (unsigned)0xFF << 8;
你得到a = 0x0000FF00
。
编译器只有两个奇怪的东西:
uint32_t a = (unsigned char)0xFF << 8;
返回= 0xFFFFFF00 uint32_t a = 0x000000FF << 8;
也返回= 0xFFFFFF00。也许它在编译器中是错误的演员......
编辑:
正如phuclv所指出的,上述解释略有不足。正确的解释是,使用uint32_t a = 0xFF << 8;
,编译器执行此操作:
0xFF
;它是int
; 0xFF00
;它是一个int,所以它是负面的uint32_t
。由于它是否定的,1
s会被添加到前面,从而产生0xFFFFFF00
与上述解释的不同之处在于,如果您撰写uint32_t a = 0xFF << 7;
,则会获得0x7F80
而不是0xFFFFFF80
。
这也解释了两个&#34;怪异的&#34;我在上一个答案的最后写的东西。
作为参考,在thread linked in the comment中有关于编译器如何解释文字的更多解释。特别是在this answer中,有一个表格,其中包含编译器为文字指定的类型。在这种情况下(无后缀,十六进制值),编译器根据符合该值的最小类型分配此类型:
int
unsigned int
long int
unsigned long int
long long int
unsigned long long int
这导致了一些更多的考虑因素:
uint32_t a = 0x7FFF << 8;
这意味着文字被解释为有符号整数;促销到更大的整数会扩展符号,因此结果为0xFFFFFF00
uint32_t b = 0xFFFF << 8;
在这种情况下,文字被解释为无符号整数。因此,提升为32位整数的结果为0x0000FF00
答案 1 :(得分:0)
[感谢Mats Petersson]
使用强制转换操作符强制编译器将0xFF视为 uint32_t 解决问题。看起来Arduino xcompiler对常量的处理方式略有不同,因为我从来没有在转换之前进行过演员。
谢谢!
答案 2 :(得分:0)
这里最重要的是,在Arduino中, int是16位类型。一切都会解释
对于uint32_t a = 0xFF << 8
:0xFF的类型为int
1 。 0xFF << 8
的结果为0xFF00,它是16位int 2 中的有符号负值。再次将int
的值分配给uint32_t
变量时,将在向上广播时将其符号扩展 3 ,因此结果变为 0xFFFFFF00U
对于以下几行
uint32_t b = 0xFF;
uint32_t c = b << 8;
0xFF在16位整数中为正,因此b
也包含0xFF。然后将其左移8位将导致0x0000FF00,因为b << 8
是uint32_t
表达式。它比int
宽,因此此处没有int
的升级
与uint32_t a = (unsigned)0xFF << 8
类似,输出为0x0000FF00,因为当转换为unsigned int
时,正0xFF仍为正。将unsigned int
上载到uint32_t
会进行零扩展,但是符号位已经为零,因此即使您执行int32_t b = 0xFF; uint32_t c = b << 8
,高位也仍然为零。与“怪异” uint32_t a = 0x000000FF << 8
相同。代替(无符号)0xFF,您可以使用完全等效的版本(但更短)0xFFU
OTOH,如果您将b声明为uint8_t b = 0xFF
或int8_t b = 0xFF
,则情况将有所不同,将发生整数提升,并且结果将与第一行(0xFFFFFF00U)相似。如果您像这样将0xFF投射到signed char
uint32_t b = (signed char)0xFF;
uint32_t c = b << 8;
然后在提升为int时将其符号扩展为0xFFFF。同样,将其强制转换为int32_t
或uint32_t
将导致符号从signed char
扩展到32位宽值0xFFFFFFFF
如果像unsigned char
中那样强制转换为uint32_t a = (unsigned char)0xFF << 8;
,则(unsigned char)0xFF
将使用零扩展名 4 提升为整数,因此结果将完全相同与uint32_t a = 0xFF << 8;
摘要:如有疑问,请查阅标准。编译器很少对您说谎
1 Type of integer literals not int by default?
整数常量的类型是对应列表的第一个,可以在其中表示其值。
Suffix Decimal Constant Octal or Hexadecimal Constant
-------------------------------------------------------------------
none int int
long int unsigned int
long long int long int
unsigned long int
long long int
unsigned long long int
2 严格来说,进入符号位是不确定的行为
3 规则是添加UINT_MAX + 1
否则,如果新类型是无符号的,则通过重复添加或减去新类型所能代表的最大值,直到该值在新类型的范围内,来转换该值。
4 如果值适合目标类型,则强制转换将始终保留输入值,因此将有符号类型转换为较宽的有符号类型将通过符号扩展来完成,并强制转换将无符号类型扩展为更宽泛的类型将通过零扩展来完成