Arduino左移不按预期工作,编译错误?

时间:2014-09-14 21:52:39

标签: c++ arduino integer arduino-uno

uint32_t a = 0xFF << 8;
uint32_t b = 0xFF;
uint32_t c = b << 8;

我正在为Uno(1.0.x和1.5)进行编译,ac显然应该是相同的值,但它们不是......至少不是在目标上运行时。我在主机上编译相同的代码,没有任何问题。

右移工作正常,左移仅在我将变量与常数移动时起作用。

有人可以证实吗?

我正在使用Visual Micro和VS2013。使用1.0.x或1.5 Arduino进行编译会导致同样的失败。

编辑:

目标:

A = 0xFFFFFF00
C = 0x0000FF00

3 个答案:

答案 0 :(得分:1)

问题与签名/无符号隐式演员有关。

使用uint32_t a = 0xFF << 8;表示

    宣布
  • 0xFF;它是signed char;
  • 有一个&lt;&lt;操作,以便将变量转换为int。由于它是一个带符号的char(因此其值为-1),因此用1填充,以保留符号。所以变量是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

编译器只有两个奇怪的东西:

  1. uint32_t a = (unsigned char)0xFF << 8;返回= 0xFFFFFF00
  2. uint32_t a = 0x000000FF << 8;也返回= 0xFFFFFF00。
  3. 也许它在编译器中是错误的演员......

    编辑:

    正如phuclv所指出的,上述解释略有不足。正确的解释是,使用uint32_t a = 0xFF << 8;,编译器执行此操作:

      宣布
    • 0xFF;它是int;
    • 有一个&lt;&lt;操作,因此这变为0xFF00;它是一个int,所以它是负面的
    • 然后将其提升为uint32_t。由于它是否定的,1 s会被添加到前面,从而产生0xFFFFFF00

    与上述解释的不同之处在于,如果您撰写uint32_t a = 0xFF << 7;,则会获得0x7F80而不是0xFFFFFF80

    这也解释了两个&#34;怪异的&#34;我在上一个答案的最后写的东西。

    作为参考,在thread linked in the comment中有关于编译器如何解释文字的更多解释。特别是在this answer中,有一个表格,其中包含编译器为文字指定的类型。在这种情况下(无后缀,十六进制值),编译器根据符合该值的最小类型分配此类型:

    1. int
    2. unsigned int
    3. long int
    4. unsigned long int
    5. long long int
    6. unsigned long long int
    7. 这导致了一些更多的考虑因素:

      • 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位类型。一切都会解释

  1. 对于uint32_t a = 0xFF << 8:0xFF的类型为int 1 0xFF << 8的结果为0xFF00,它是16位int 2 中的有符号负值。再次将int的值分配给uint32_t变量时,将在向上广播时将其符号扩展 3 ,因此结果变为 0xFFFFFF00U

  2. 对于以下几行

    uint32_t b = 0xFF;
    uint32_t c = b << 8;
    

    0xFF在16位整数中为,因此b也包含0xFF。然后将其左移8位将导致0x0000FF00,因为b << 8uint32_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 = 0xFFint8_t b = 0xFF,则情况将有所不同,将发生整数提升,并且结果将与第一行(0xFFFFFF00U)相似。如果您像这样将0xFF投射到signed char

uint32_t b = (signed char)0xFF;
uint32_t c = b << 8;

然后在提升为int时将其符号扩展为0xFFFF。同样,将其强制转换为int32_tuint32_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 如果值适合目标类型,则强制转换将始终保留输入值,因此将有符号类型转换为较宽的有符号类型将通过符号扩展来完成,并强制转换将无符号类型扩展为更宽泛的类型将通过零扩展来完成