0
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
4

3 回答 3

1

该问题与有符号/无符号隐式转换有关。

uint32_t a = 0xFF << 8;的意思是

  • 0xFF被宣布;它是一个signed char
  • 有一个 << 操作,以便将变量转换为 int。因为它是一个有符号字符(所以它的值是-1)它用 1 填充,以保留符号。所以变量是0xFFFFFFFF;
  • 它被转移了,所以a = 0xFFFFFF00.

注意:这有点错误,请参阅下面的“更正确”版本

如果您想重现相同的行为,请尝试以下代码:

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;返回 a = 0xFFFFFF00
  2. uint32_t a = 0x000000FF << 8;也返回 a = 0xFFFFFF00。

也许这是编译器中的错误转换....

编辑:

正如 phuclv 所指出的,上述解释略有错误。正确的解释是,uint32_t a = 0xFF << 8;编译器执行以下操作:

  • 0xFF被宣布;它是一个int
  • 有一个 << 操作,因此变成0xFF00; 它是一个 int,所以它是负数
  • 然后将其提升为uint32_t. 因为它是负数,所以1在前面加上 s,结果是0xFFFFFF00

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

这也解释了我在上一个答案末尾写的两个“奇怪”的东西。

作为参考,在评论中链接的线程中,有更多关于编译器如何解释文字的解释。特别是在这个答案中有一个表,其中包含编译器分配给文字的类型。在这种情况下(无后缀,十六进制值)编译器根据适合该值的最小类型分配此类型:

  1. int
  2. unsigned int
  3. long int
  4. unsigned long int
  5. long long int
  6. unsigned long long int

这导致了更多的考虑:

  • uint32_t a = 0x7FFF << 8;这意味着文字被解释为有符号整数;提升到更大的整数会扩展符号,因此结果是0xFFFFFF00
  • uint32_t b = 0xFFFF << 8;在这种情况下,文字被解释为无符号整数。因此,提升到 32 位整数的结果是0x0000FF00
于 2014-09-15T17:19:11.160 回答
0

这里最重要的是,在 Arduino 中int 是 16 位类型。这将解释一切

  1. 对于uint32_t a = 0xFF << 8: 0xFF 是类型int1结果 0xFF00 是 16 位 int 20xFF << 8中的有符号负值。再次将值分配给变量时,向上转换时将符号扩展为3,因此结果变为0xFFFFFF00Uintuint32_t

  2. 对于以下行

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

    0xFF在 16 位 int 中为正数b,因此也包含 0xFF。然后将其左移 8 位结果为 0x0000FF00,因为b << 8它是一个uint32_t表达式。它比这里更广泛,int所以这里没有促销int活动

同样,uint32_t a = (unsigned)0xFF << 8输出为 0x0000FF00,因为转换为正的 0xFFunsigned int仍然是正的。向上转换unsigned intuint32_t进行零扩展,但符号位已经为零,因此即使您这样做int32_t b = 0xFF; uint32_t c = b << 8,高位仍然为零。与“奇怪” uint32_t a = 0x000000FF << 8相同。而不是 (unsigned)0xFF 您可以使用完全相同的版本(但更短)0xFFU

OTOH,如果您将 b 声明为uint8_t b = 0xFFor int8_t b = 0xFFthen 情况将有所不同,则会发生整数提升,结果将类似于第一行 (0xFFFFFF00U)。如果你投 0xFF 来signed char喜欢这个

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

然后在提升为 int 时,它将被符号扩展为 0xFFFF。类似地,将其转换为int32_tuint32_t将导致从signed char32 位宽值 0xFFFFFFFF的符号扩展

如果您改为unsigned charlike in uint32_t a = (unsigned char)0xFF << 8;,则将(unsigned char)0xFF使用零扩展名将4提升为 int ,因此结果将与uint32_t a = 0xFF << 8;

总结:如有疑问,请查阅标准。编译器很少对你说谎


1 默认情况下不是 int 的整数文字类型?

整数常量的类型是可以表示其值的对应列表中的第一个。

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如果值适合目标类型,转换将始终保留输入值,因此将有符号类型转换为更广泛的有符号类型将通过符号扩展完成,将无符号类型转换为更广泛的类型将通过零扩展

于 2019-02-17T15:20:02.783 回答
-1

[归功于 Mats Petersson]

使用强制转换运算符强制编译器将 0xFF 视为uint32_t解决了该问题。似乎 Arduino xcompiler 对待常量的方式略有不同,因为我从未在轮班之前进行过强制转换。

谢谢!

于 2014-09-14T22:54:51.837 回答