【问题标题】:Make a Integer from 6 bytes or more using C++使用 C++ 从 6 个字节或更多字节生成整数
【发布时间】:2018-08-07 15:28:56
【问题描述】:

我是C++ 编程的新手。我正在尝试实现一个代码,通过它我可以从6 或更多individual bytes 中生成一个整数值。

我已经为 4 bytes 实现了相同的功能,它正在工作

我的 4 字节代码:

char *command = "\x42\xa0\x82\xa1\x21\x22";
__int64 value;
value = (__int64)(((unsigned char)command[2] << 24) + ((unsigned char)command[3] << 16) + ((unsigned char)command[4] << 8) + (unsigned char)command[5]);
printf("%x  %x  %x  %x  %x",command[2], command[3], command[4], command[5], value);

使用此代码,value 的值是82a12122,但是当我尝试执行 6 字节时,结果是错误的。

6 字节代码:

char *command = "\x42\xa0\x82\xa1\x21\x22";
__int64 value;
value = (__int64)(((unsigned char)command[0] << 40) + ((unsigned char)command[1] << 32) + ((unsigned char)command[2] << 24) + ((unsigned char)command[3] << 16) + ((unsigned char)command[4] << 8) + (unsigned char)command[5]);
printf("%x  %x  %x  %x  %x  %x  %x", command[0], command[1], command[2], command[3], command[4], command[5], value);

value 的输出值是82a163c2 这是错误的,我需要42a082a12122。 那么谁能告诉我如何获得预期的输出以及6 Byte 代码有什么问题。

提前致谢。

【问题讨论】:

  • (unsigned char)command[0] &lt;&lt; 40,你的无符号字符,将被提升为int,在你的情况下,它的字节数比int64_t 少。
  • 不要使用%x,对于字符使用%hhx,对于int64 %llx in printf
  • 您必须在转换前将其转换为_int64
  • @IlyaBursov:从技术上讲,如果你想小心点,you'd use the inttypes.h macros like PRIx64 而不是猜测像ll 这样的相对宽度前缀和固定位宽类型之间的对应关系。或者因为它是 C++,你只需使用 iostreamstd::cout 并完全避免这个问题。
  • 这实际上是 C++ 吗?对我来说看起来像是低级 C。

标签: c++ integer byte-shifting


【解决方案1】:

在移位之前将每个字节转换为足够大的无符号类型。即使在整体提升(到unsigned int)之后,该类型也不足以移动超过 32 个字节(在通常情况下,这似乎适用于您)。

见这里演示:https://godbolt.org/g/x855XH

unsigned long long large_ok(char x)
{
    return ((unsigned long long)x) << 63;
}

unsigned long long large_incorrect(char x)
{
    return ((unsigned long long)x) << 64;
}


unsigned long long still_ok(char x)
{
    return ((unsigned char)x) << 31;
}

unsigned long long incorrect(char x)
{
    return ((unsigned char)x) << 32;
}

简单来说:

移位运算符自动将其操作数提升为int/unsigned int。这就是您的四字节版本有效的原因:unsigned int 对于您的所有班次来说都足够大。但是,(在您的实现中,与大多数常见的一样)它只能保存 32 位,如果您移动超过 32 位,编译器将不会自动选择 64 位类型(编译器不可能知道) .

如果您对移位操作数使用足够大的整数类型,则移位将具有较大的类型作为结果,并且移位将按照您的预期进行。

如果您打开警告,您的编译器可能还会向您抱怨您移动的位数超过了类型的位数,因此总是为零(参见演示)。

(提到的位数当然是实现定义的。)


最后一点:以双下划线 (__) 或下划线 + 大写字母开头的类型保留用于实现 - 使用它们在技术上并不“安全”。现代 C++ 为您提供了诸如 uint64_t 之类的类型,它们应该具有规定的位数 - 请改用这些类型。

【讨论】:

  • 仍然得到相同的答案,我的表达是 value = (((unsigned long long)command[0]
  • 您在 32 位移位时缺少它。那个也太大了。但实际上,您应该为所有这些都这样做以保持一致性。也请注意我的编辑。
  • 我可以用 unsigned long long 先生全部投射吗?
  • 是的,或者只使用uint64_t。它的输入/读取时间更短,并且保证长度为 64 位,这与 unsigned long long 不同,它不保证是(但通常是)64 位。
  • 先生,我听说在进行字节移位时需要字节序,请您解释一下。
【解决方案2】:

您的班次溢出字节,并且您没有正确打印整数。

此代码有效: (注意打印格式以及如何在uint64_t 中完成转换)

#include <stdio.h>
#include <cstdint>

int main()
{
    const unsigned char *command = (const unsigned char *)"\x42\xa0\x82\xa1\x21\x22";
    uint64_t value=0;
    for (int i=0; i<6; i++)
    {
        value <<= 8;
        value += command[i];
    }
    printf("%x  %x  %x  %x  %x  %x  %llx",
             command[0], command[1], command[2], command[3], command[4], command[5], value);
}

【讨论】:

  • @MaxLanghof 固定为 uint64_t
猜你喜欢
  • 1970-01-01
  • 2014-05-13
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多