位移不正确?

问题描述:

我正在用C语言编程8051,使用Si Labs IDE。我目前有三个字节:address_byte3, address_byte2, and address_byte1。然后我初始化的变量address_sum是一个unsigned long int然后做就可以了以下操作...位移不正确?

address_sum=(address_byte3<<16)+(address_byte2<<8)+(address_byte1); 

此操作将导致我相信,值加载到address_sum如果address_byte3, address_byte2, & address_byte10x92, 0x56, & 0x78,分别是0xXX925678 。相反,我得到的值为0xXX005678。我的逻辑听起来很合理,但我又是编写代码的人,所以我有点偏见,可能被我自己的无知所蒙蔽。有没有人有解决方案或解释为什么address_byte的价值是“失去”?

谢谢。

+2

你的系统上有多宽的int?它可能是16位。算术运算是通过将操作数提升为'int'来完成的,所以用'16'移位会给你一个不错的零... –

+1

@EugeneSh .:移动16会给你_undefined behavior_ –

+0

8051看起来是“Intel_MCS- 51“,它有8位寄存器? https://en.wikipedia.org/wiki/Intel_MCS-51 –

当对它们进行计算时,比int短的变量被提升为int。看起来你的类型是16位的,所以将它移动16位并不正确。

你应该明确地投下变量的结果类型(unsigned long):

address_sum = ((unsigned long)address_byte3<<16) + 
       ((unsigned long)address_byte2<<8) + 
       (unsigned long)address_byte1; 

最后铸造是多余的,但不痛。

+1

应注意字节实际上是'signed char' –

+0

这工作,并解决了我在我的代码中看到的每一个其他错误。谢谢。我不知道我的'char'变量会被提升为'int'。因此,在对它们进行按位操作时,投射它会阻止我失去任何一点点的东西?另外,我在例程中强制这样做,这是否意味着这只会在例程中发生,并且变量不会在'main()'中受到影响? –

+1

Casting不会影响铸造变量。 –

16位int/unsigned的移位,以及@anatolyg的解释只会导致16位答案。

我避免强制转换,作为一般的升级方案,有时它可能会因为代码随着时间的推移而变化,并且维护者使用更宽的操作数。

备选方案:

((type_of_target) 1) *:这将确保各操作是至少目标的宽度。

unsigned long address_sum; 
... 
address_sum = (1UL*address_byte3<<16) + (1UL*address_byte2<<8) + address_byte1; 

分配到目的地,然后运行:

address_sum = address_byte3; 
address_sum = address_sum << 8 + address_byte2; 
address_sum = address_sum << 8 + address_byte1; 

手法高明,以为不愉快的看1行的选择。回想* +较高位次比转向

address_sum = (0*address_sum + address_byte3 << 16) + 
       (0*address_sum + address_byte2 << 8) + address_byte1; 

考虑@Eugene Sh.关注,并采用8位无符号“字节”。

我的首选是chux上的变体

更大的声明地址; 字节声明a,b,c;

address =a; address<<=8; 
address|=b; address<<=8; 
address|=c; 

尽管是最详细的答案,迄今为止所有的答案应该优化成基本相同的代码。但必须测试特定的编译器才能看到。无论如何,8051可以每指令一次移动多位吗?不记得了。

+0

是的,你可以每个指令移动多个位。我确信这就是你在上面的例子中所做的事情。我的问题是我没有意识到我的变量被提升为int,并且当我移动了16位时,最上面的变量丢失了,因此给我一个未定义的行为。 –

+0

我说的是指令集,一些指令集的移位指令是一次一位,你必须多次调用指令。我经历过这么多的指令集,我不记得几年来我没有用过的每一个细节的细微差别...... 8051来自每个班次一位的时代,但8086是一个类似的时代,有一个多位转换选项。 –

+0

是的,它使用每位指令(rlc或rrc基本上)一位移位,但由于寄存器是8位,移位实际上不会发生在这里,寄存器可能不会被使用,但是三个存储位置,我不知道你会使用24位地址,但这是另一回事...... –