我遇到了这段存在的C代码。我很难理解它。
我非常希望将缓冲区中传递的4字节无符号值(小端格式)读入类型为" long"的变量。
此代码在64位字大小的小端x86机器上运行 - 其中sizeof(long)为8个字节。 我的猜测是这个代码也打算在32位x86机器上运行 - 因此为了存储来自四字节输入数据的值,使用long类型的变量而不是int。
我有一些疑问,并在代码中添加了评论以表达我理解的内容,或者我不知道的内容: - )
请在该背景下回答以下问题
void read_Value_From_Four_Byte_Buff( char*input)
{
/* use long so on 32 bit machine, can still accommodate 4 bytes */
long intValueOfInput;
/* Bitwise and of input buffer's byte 0 with 0xFF gives MSB or LSB ?*/
/* This code seems to assume that assignment will store in rightmost byte - is that true on a x86 machine ?*/
intValueOfInput = 0xFF & input[0];
/*left shift byte-1 eight times, bitwise "or" places in 2nd byte frm right*/
intValueOfInput |= ((0xFF & input[1]) << 8);
/* similar left shift in mult. of 8 and bitwise "or" for next two bytes */
intValueOfInput |= ((0xFF & input[2]) << 16);
intValueOfInput |= ((0xFF & input[3]) << 24);
}
我的问题
1)输入缓冲区应该在&#34; Little endian&#34;中。但是从代码看起来这里的假设是它读入字节0 = MSB,字节1,字节2,字节3 = LSB。我之所以这么认为是因为代码从字节0开始读取字节,后续字节(1以后)在左移后放置在目标变量中。这是怎么回事,或者我弄错了?
2)我觉得这是一种令人费解的做事方式 - 是否有更简单的替代方法将值从4字节缓冲区复制到长变量中?
3)假设&#34;此代码将在64位机器上运行&#34;会对我能够轻易做到这一点有什么影响吗?我的意思是让所有这些麻烦都与字大小无关(我认为它现在与字大小无关 - 虽然不确定)?
感谢你的启示:-)
答案 0 :(得分:3)
你倒退了。当你离开时,你会进入更重要的位。所以(0xFF & input[3]) << 24)
将字节3放入MSB。
这是在标准C中执行此操作的方法.POSIX具有从网络字节顺序转换为本机32位整数的函数ntohl()
,因此这通常用于Unix / Linux应用程序
除非您使用unsigned long
而不是long
,否则在64位计算机上这不会完全相同。按照目前的写法,input[3]
的最高位将被放入结果的符号位(假设是二进制补码机器),因此您可以获得负面结果。如果long
是64位,则所有结果都是正数。
答案 1 :(得分:2)
从代码中,字节0是LSB,字节3是MSB。但是有一些错别字。这些行应该是
intValueOfInput |= ((0xFF & input[2]) << 16);
intValueOfInput |= ((0xFF & input[3]) << 24);
您可以通过删除0xFF但在参数类型中使用“unsigned char”类型来缩短代码。
要缩短代码,您可以执行以下操作:
long intValueOfInput = 0;
for (int i = 0, shift = 0; i < 4; i++, shift += 8)
intValueOfInput |= ((unsigned char)input[i]) << shift;
答案 2 :(得分:2)
uint32_t x = *(uint32_t *)input;
即可,但这假设您的机器是小端,我认为根据C标准,它可能是未定义的行为。uint32_t
和int32_t
等类型,以便更轻松地推断您的代码是否适用于不同的体系结构。您只需要包含C99的stdint.h
标头即可使用这些类型。此函数最后一行的右侧可能会显示undefined behavior,具体取决于输入中的数据:
((0xFF & input[3]) << 24)
问题是(0xFF & input[3])
将是签名的int
(因为整数提升)。 int
可能是32位,并且您将其向左移动到左侧,结果值可能无法在int
中表示。 C standard表示这是未定义的行为,您应该尽量避免这种行为,因为它为编译器提供了执行任何操作的许可,您将无法预测结果。
解决方案是在使用强制转换之前将其从int
转换为uint32_t
。
最后,变量intValueOfInput
被写入但从未使用过。你不应该把它归还或存放在某个地方吗?
考虑到所有这些,我会改写这样的函数:
uint32_t read_value_from_four_byte_buff(char * input)
{
uint32_t x;
x = 0xFF & input[0];
x |= (0xFF & input[1]) << 8;
x |= (0xFF & input[2]) << 16;
x |= (uint32_t)(0xFF & input[3]) << 24;
return x;
}