【问题标题】:Problem of converting byte order for unsigned 64-bit number in C在 C 中转换无符号 64 位数字的字节顺序问题
【发布时间】:2020-02-28 18:17:40
【问题描述】:

我正在玩 little endian/big endian 转换,发现 a 有点令人困惑但也很有趣。

在第一个示例中,使用位移来转换uint32_t 类型的字节顺序没有问题。它基本上将uint32_t 整数转换为uint8_t 数组并尝试访问每个字节和位移位。

示例 #1:

uint32_t htonl(uint32_t x)
{
    uint8_t *s = (uint8_t*)&x;
    return (uint32_t)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
}

但是,如果我尝试在下面的 uint64_t 上做类似的事情,编译器会抛出关于“s[0] 宽度小于 56 位”的警告,如下面的示例 #2 所示。

示例 #2:

uint64_t htonl(uint64_t x)
{
    uint8_t *s = (uint8_t*)&x;
    return (uint64_t)(s[0] << 56 ......);
}

为了使其工作,我必须将每个字节提取到 uint64_t 中,这样我就可以进行位移而不会出现任何错误,如下面的示例 #3 所示。

示例 #3:

uint64_t htonll2(uint64_t x)
{
    uint64_t byte1 = x & 0xff00000000000000;
    uint64_t byte2 = x & 0x00ff000000000000;         
    uint64_t byte3 = x & 0x0000ff0000000000;
    uint64_t byte4 = x & 0x000000ff00000000;
    uint64_t byte5 = x & 0x00000000ff000000;
    uint64_t byte6 = x & 0x0000000000ff0000;                                                                                              
    uint64_t byte7 = x & 0x000000000000ff00;
    uint64_t byte8 = x & 0x00000000000000ff;

    return (uint64_t)(byte1 >> 56 | byte2 >> 40 | byte3 >> 24 | byte4 >> 8 |
                      byte5 << 8  | byte6 << 24 | byte7 << 40 | byte8 << 56);
}

我对@9​​87654329@ 和Example #2 有点困惑,据我了解,s[i] 的大小均为uint8_t,但不知何故,如果它只移动 32 位或更少,则在全部,但是在像 56 位一样移动时会出现问题。我在带有 GCC 8.3.0 的 Ubuntu 上运行这个程序。

在这种情况下,编译器是否会将s[i] 隐式转换为 32 位数字?当我向其中添加调试消息时,sizeof(s[0]) 为 1。

【问题讨论】:

    标签: c gcc uint32


    【解决方案1】:

    在表达式中使用时,类型小于 int 的值被提升int。假设 int 在您的平台上是 32 位的,这在转换 32 位值时适用于大多数情况。如果您将 1 位移入符号位,则它不起作用的时间。

    在 64 位的情况下,这意味着您试图移动一个超过其位长度的值,这是未定义的行为。

    在这两种情况下,您都需要将每个字节转换为 uint64_t 以使班次正常工作。

    【讨论】:

      【解决方案2】:

      s[0] 表达式有一个 8 位宽的整数类型,当被移位运算符操作时,它被提升为一个 32 位无符号整数 - 所以第一个示例中的 s[0] &lt;&lt; 24 工作正常,因为移位24 不超过uint 长度。

      OTOH 移位 56 位会将数据移动到结果长度之外,因为偏移量超过了整数的长度,因此它肯定会导致信息丢失,因此会发出警告。

      【讨论】:

      • uint8_t 将被提升为 int。它将是有符号的,而不是无符号的,它将是 int 的任何宽度,现在通常是 32 位,但 C 标准允许为 16 位或更多。这不会“正常”工作,因为如果uint8_t 的高位打开且int 为32 位,则移位24 位将溢出int 中可表示的值范围,并且行为不是由 C 标准定义。
      猜你喜欢
      • 1970-01-01
      • 2013-08-19
      • 2011-04-21
      • 1970-01-01
      • 2020-01-13
      • 1970-01-01
      • 2011-10-21
      • 2012-02-17
      • 2013-01-21
      相关资源
      最近更新 更多