基于四个 16 位整数创建一个 64 位整数

Creating a 64-bit Integer Based Off of Four 16-bit Integers

我正在尝试使用按位运算符将四个 16 位整数转换为 C 中的一个 64 位整数(OS 是 Linux 运行 Ubuntu 20.04 服务器)。我的总体目标是能够将四个 32 位整数转换为一个 128 位整数(来自 in6_addr 结构内的四个 32 位整数的 IPv6 源 IP 地址)。但是,在下面的示例中,我使用了四个 16 位整数和一个 64 位整数,因为我在使用 GCC 分配 128 位整数时遇到了问题(我假设我可以将下面的相同逻辑应用到我的总体目标)。

在这种情况下,我的系统的字节顺序也是小端。但是,如果您也想展示如何在大端上执行此操作,请随意!

这是我当前在 C 中的测试代码:

#include <stdio.h>
#include <inttypes.h>

int main()
{
    uint16_t nums[4];
    uint64_t num = 2310051230312123321;
    uint64_t mainnum = 0;

    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        nums[0] = num >> 0;     // xxxx ---- ---- ----
        nums[1] = num >> 16;    // ---- ---- ---- xxxx
        nums[2] = num >> 32;    // ---- ---- xxxx ----
        nums[3] = num >> 48;    // ---- xxxx ---- ----
    #else
        /*
        nums[0] = num << 0;
        nums[1] = num << 16;
        nums[2] = num << 32;
        nums[3] = num << 48;
        */
    #endif

    printf("Num => %" PRIu64 "\nNum #1 => %" PRIu16 "\nNum #2 => %" PRIu16 "\nNum #3 => %" PRIu16 "\nNum #4 => %" PRIu16 "\n\n", num, nums[0], nums[1], nums[2], nums[3]);

    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        mainnum = ((uint64_t)(nums[0] >> 16 | nums[1]) >> 48) | (nums[3] << 16 | nums[2]);
    #else
        /* ... */
    #endif

    uint16_t other[4];

    other[0] = mainnum >> 0;
    other[1] = mainnum >> 16;
    other[2] = mainnum >> 32;
    other[3] = mainnum >> 48;

    printf("Num => %" PRIu64 "\nNum #1 => %" PRIu16 "\nNum #2 => %" PRIu16 "\nNum #3 => %" PRIu16 "\nNum #4 => %" PRIu16 "\n", mainnum, other[0], other[1], other[2], other[3]);

    return 0;
}

程序输出如下:

Num => 2310051230312123321
Num #1 => 19385
Num #2 => 54197
Num #3 => 62298
Num #4 => 8206

Num => 537850714
Num #1 => 62298
Num #2 => 8206
Num #3 => 0
Num #4 => 0

我希望 nummainnum 相同。

我知道我不理解这里的某些东西,而且我对按位运算符没有太多经验。

mainnum = ((uint64_t)(nums[0] >> 16 | nums[1]) >> 48) | (nums[3] << 16 | nums[2]);

据我了解,我从 nums[0] 开始,我假设这应该用这个值填充前 16 位。从这里开始,我向右移动 16 位,将我们放在 48 位偏移处,然后用 nums[1] 替换之后的 16 位(我认为其中最重要的是小端)。

从这里开始,我将 mainnum 向右移动 48 位,使我们处于 16 位偏移量。然后我将接下来的 16 位替换为 nums[3] 并向左移动 16 位并将其余的替换为 nums[2].

我确实需要更多地练习按位运算符(它在我的待办事项列表中)并且我确定我在这里做的事情很傻。我只是想看看是否有人可以纠正我做错了什么。

非常感谢任何帮助,感谢您抽出宝贵时间!

您需要用于从 num 创建 nums 的 same/corresponding 索引和移动量:

nums[0] = num >> 0;                 // xxxx ---- ---- ----
nums[1] = num >> 16;                // ---- ---- ---- xxxx
nums[2] = num >> 32;                // ---- ---- xxxx ----
nums[3] = num >> 48;                // ---- xxxx ---- ----

并且,每个 术语需要一个 (uint64_t) 强制转换为 64 位。否则,移位将超过赋值右侧用于中间项的大小(例如,它们将使用 int and/or unsigned int)。

mainnum |= (uint64_t) nums[0] << 0;
mainnum |= (uint64_t) nums[1] << 16;
mainnum |= (uint64_t) nums[2] << 32;
mainnum |= (uint64_t) nums[3] << 48;

我更喜欢上面的,因为它是 cleaner/clearer 并且 [在优化时] 将生成与使用单个语句相同的代码:

mainnum =
    (uint64_t) nums[0] << 0 |
    (uint64_t) nums[1] << 16 |
    (uint64_t) nums[2] << 32 |
    (uint64_t) nums[3] << 48;