首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C/C++中重构数字

在C/C++中重构数字
EN

Stack Overflow用户
提问于 2010-08-09 09:03:06
回答 3查看 195关注 0票数 2

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

例如:

代码语言:javascript
复制
printf("%02X", data);

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

代码语言:javascript
复制
printf("%02X", data & 0xff);

下面是我在字节流中看到的一个简化示例。其中bytes是我从套接字读取的字节流。

代码语言:javascript
复制
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;
}

以下哪项输出:

代码语言:javascript
复制
x.num=500
method1 = 4294967284
method2 = 244
method3 = 500

只有最后的摘录才是正确的。我建立数字的方法是最优的吗?我也尝试了一个变量的memcpy,但这同样不可靠。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-08-09 09:07:36

当有符号数据类型被转换为更高的数据类型时,最高有效位被用作符号位。你的工会里应该有unsigned char。在你的例子中,500 = 256 + 244 = 0x1f4,字节244设置了最高有效位,所以当提升时变成0xfffffff4。

票数 4
EN

Stack Overflow用户

发布于 2010-08-09 09:06:53

为什么不在union中使用unsigned char bytes[4]呢?如果没有符号规范,你就不知道你的char是有符号的还是无符号的(取决于平台和编译器),所以它们的算术运算会产生特殊的结果也就不足为奇了。

如果您的编译器确实决定将您的“神秘字符数据”视为有符号而不是无符号,那么您可能已经观察到0xFF可能是由于符号扩展造成的。

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

票数 1
EN

Stack Overflow用户

发布于 2010-08-09 09:14:48

在小端体系结构中,数字500 (256 + 244)将存储为:

代码语言:javascript
复制
+-----------+-----------+-----------+-----------+
| 244(0xf4) |   1(0x01) |   0(0x00) |   0(0x00) |
+-----------+-----------+-----------+-----------+

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

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

这意味着244 (-120xf4)将被符号扩展到0xfffffff4。这可能会把你的|+解决方案搞得一团糟。

您将获得的值为:

代码语言:javascript
复制
x0       0xfffffff4  
x1<<8    0x00000100
x2<<16   0x00000000
x3<<24   0x00000000

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

方法2添加了它们,所以您最终得到了0x1000000f4,它当然会包装,去掉高位字节,只剩下0xf4或244。

在方法3中,符号扩展仍然发生,但在您使用0xff &它之前。正是&操作逆转了签名扩展效果,并将您的0xfffffff4重新转换为0xf4

正如其他人已经提到的,显式地使用unsigned char。这将防止在升级到更大的整数类型时发生符号扩展。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3436749

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档