【问题标题】:bit manipulation in c giving unexpected resultsc中的位操作会产生意想不到的结果
【发布时间】:2014-03-25 18:09:56
【问题描述】:

我有这个程序:

#include <stdio.h>

int main(void)
{
    unsigned char unit_id[] = { 0x2B, 0xC, 0x6B, 0x54}; // 8-bit (1 byte)
    unsigned long long int unit_id_val; //64-bit (8 bytes)
    int i;

    // loops 4 times
    for(i=0;i<sizeof(unit_id)/sizeof(char);i++){
      unit_id_val |= unit_id[i] << (8 * i);
    }

    printf("the unit id is %llu\n", unit_id_val);
    return 0;
}

十六进制到二进制的转换:

0x2B = 00101011
0xC  = 00001100
0x6B = 01101011
0x54 = 01010100

unit_id_val 为 8 个字节(我在下面为 unit_id_val 使用 5 个字节以简化事情)

1) 第一次迭代 8*0=0 所以不会发生左移:

00101011 = 00101011 << 0
00000000 00000000 00000000 00000000 00000000 |= 00101011

所以结果应该是:

00000000 00000000 00000000 00000000 00101011

2) 第二次迭代 8*1=8,所以将 unsigned char 0xC 的所有位左移 8:

00000000 = 00101011 << 8
00000000 00000000 00000000 00000000 00101011 |= 00000000

所以结果应该是:

00000000 00000000 00000000 00000000 00101011

3) 第三次迭代 8*2=16,因此将 unsigned char 0x6B 的所有位左移 16:

00000000 = 01101011 << 16
00000000 00000000 00000000 00000000 00101011 |= 00000000

所以结果应该是:

00000000 00000000 00000000 00000000 00101011

4) 第四次迭代 8*3=24,因此将 unsigned char 0x54 的所有位左移 32:

00000000 = 01010100 << 32
00000000 00000000 00000000 00000000 00101011 |= 00000000

所以结果应该是:

00000000 00000000 00000000 00000000 00101011 

00101011 是 43

但是当你运行这个程序时,你会得到

1416301611

这是二进制:

00010100 00010110 00110000 00010110 00010001

我不明白这里的一些东西。我通过在评估左移运算符

【问题讨论】:

  • by 32&lt;&lt; 32?
  • 一个printf("%x\n", unit_id[i] &lt;&lt; (8 * i)); 被扔进循环会告诉你你的假设在哪里是错误的。

标签: c bit-manipulation


【解决方案1】:

00000000 = 00101011 &lt;&lt; 8

好的,首先你的第二个元素是0x0C(即二进制00001100而不是00101011),所以你实际上是在做:

(unsigned char) 0x0C  << 8

并且这个表达式的结果不是0而是0x0C00,因为按位&lt;&lt;运算符对其左操作数进行整数提升,所以它实际上等价于:

(int) (unsigned char) 0x0C << 8

【讨论】:

  • @JohnMerlino 不,&lt;&lt; 表达式的结果类型是提升后的左操作数的类型。所以这里unit_id[i] &lt;&lt; (8 * i) 的类型是int,而不是unsigned char
【解决方案2】:

您永远不会初始化unit_id_val,然后您将|= 移位的字节值放入其中,因此未初始化值中碰巧设置的任何位仍将被设置,因此您的输出看起来像随机垃圾。添加

unit_id_val = 0;

在你的循环之前。

此外,每当您在 C 中执行任何操作时,操作数总是会通过标准转换进行转换。特别是,这意味着任何小于int 的整数类型都将首先转换为int。所以即使unsigned char只有8位,当你做unit_id[i] &lt;&lt; (8 * i)时,unit_id[i]的8位值将在移位前转换为int(在你的机器上大概是32位)。没有办法对小于 C 中的 int 的整数进行任何类型的计算——即使你强制转换它们,它们也会被隐式转换回 int

【讨论】:

  • “垃圾”或“垃圾”可能不够苛刻。在安腾上,你可能会得到signaling_not_a_value,所以你不能读它(只适用于寄存器)!!
【解决方案3】:

看到这一行

unit_id_val |= unit_id[i] << (8 * i);
unit_id_val = unit_id_val | unit_id[i] << (8 * i);

问题是您使用的变量仅声明但未初始化,我们知道在 c 中未初始化的变量默认值为垃圾或垃圾。所以每次毫无疑问你都会得到一些不可预测的价值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-02
    • 2019-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-05
    相关资源
    最近更新 更多