用C / C ++重写数字

时间:2010-08-09 01:03:06

标签: c++

我有一个从套接字(小端)读入的字节流。 有人能告诉我为什么只有下面的方法中的最后一个给出正确的答案? 我怀疑它与进位位有关,但不确定。我总是发现这个时候 以十六进制形式打印二进制数据

e.g。

printf("%02X", data);

它有时会在前面用0xff打印出有趣的值。修复它的方法似乎 这样做。当数据也是char数据类型时,偶尔会发生这种情况:

printf("%02X", data & 0xff);

这是我用字节流看到的简化示例。 字节是我从套接字读取的字节流。

int main(int argc, char* argv[])
{
    union {
        unsigned int num;
        char bytes[4];
    } x;

    x.num = 500;
    printf("x.num=%u\n", x.num);

    unsigned int method1 = x.bytes[0] | (x.bytes[1] << 8) | (x.bytes[2] << 16) | (x.bytes[3] << 24);
    printf("method1 = %u\n", method1);

    unsigned int method2 = x.bytes[0] + (x.bytes[1] << 8) + (x.bytes[2] << 16) + (x.bytes[3] << 24);
    printf("method2 = %u\n", method2);

    unsigned int method3 = (x.bytes[0] & 0xff | (x.bytes[1] & 0xff) << 8 
                            | (x.bytes[2] & 0xff) << 16 | (x.bytes[3] & 0xff) << 24);
    printf("method3 = %u\n", method3);

    return 0;
}

哪个输出:

x.num=500
method1 = 4294967284
method2 = 244
method3 = 500

只有最后一次提取实际上是正确的。 我的方法是建立最优的数字吗?我还尝试了memcpy变量,但又不可靠。

3 个答案:

答案 0 :(得分:4)

当将有符号数据类型转换为更高数据类型时,最高有效位用作符号位。你的工会应该有unsigned char。在你的情况下500 = 256 + 244 = 0x1f4并且字节244具有最高有效位设置,所以当提升变为0xfffffff4时。

答案 1 :(得分:1)

为什么不在unsigned char bytes[4]中使用union?如果没有签名规范,您不知道您的char是签名还是未签名(取决于平台和编译器),因此对它们进行算术会产生特殊结果并不奇怪。

如果您的编译器确实决定将您的“神秘字符数据”视为已签名而非无符号,则您可能观察到的0xFF可能是由于符号扩展所致。

根据经验,当char用于表示“用于进一步处理或显示目的的字节”时,我建议始终使用unsigned char来确定 - 我不记得何时上一次是我真的想要一个签名的字符! - )

答案 2 :(得分:0)

在小端架构中,数字500256 + 244)将存储为:

+-----------+-----------+-----------+-----------+
| 244(0xf4) |   1(0x01) |   0(0x00) |   0(0x00) |
+-----------+-----------+-----------+-----------+

并且,因为您自己使用char,所以C标准没有指定它是有符号还是无符号(它是实现定义的)。在您的情况下,它似乎已签名。

  

除此之外:当您将“瘦”数据值加载到更宽的数据值时,符号扩展以二进制补码编码发生。在细数的最高位为1(表示负数)的情况下,这扩展到更宽类型中的所有更高位。这样做的原因是为了保持数字的性质。例如,8位中的-12为0xf4,16位为0xfff4,而256位为0xfffffffffffffffffffffffffffffff4

这意味着244-120xf4)将被转换为0xfffffff4。这可能会严重破坏您的|+解决方案。

您将获得的价值是:

x0       0xfffffff4  
x1<<8    0x00000100
x2<<16   0x00000000
x3<<24   0x00000000

方法1使用|,因此最终得到0xfffffff4(x1中的另一位已经在x0中设置,因此它不受影响,x2 / x3全部为零),这是{{ 1}}作为4294967284

方法2添加它们,以便最终得到unsigned int当然包装,丢弃高字节并留下0x1000000f4或244。

在方法3中,符号扩展名仍会发生,但之前 0xf4 &。这是0xff操作,可以反转符号扩展的效果,并将&转回0xfffffff4

正如其他人已经提到的那样,明确使用0xf4。这可以防止在升级到更大的整数类型时发生符号扩展。