将uint64_t转换为uint8_t [8]

Jan*_*her 4 c++ boost

如何uint64_tuint8_t[8]不丢失C++信息的情况下转换为?

我尝试了以下方法:

uint64_t number = 23425432542254234532;
uint8_t result[8];
for(int i = 0; i < 8; i++) {
    std::memcpy(result[i], number, 1);
}
Run Code Online (Sandbox Code Playgroud)

eer*_*ika 9

你快到了.首先,文字23425432542254234532太大而不适合uint64_t.

其次,从文档中可以看出,std::memcpy有以下声明:

void * memcpy ( void * destination, const void * source, size_t num );
Run Code Online (Sandbox Code Playgroud)

如您所见,它将指针(地址)作为参数.不是uint64_t,也不是uint8_t.您可以使用address-of运算符轻松获取整数的地址.

但是,您只是将整数的第一个字节复制到每个数组元素中.您需要在每次迭代中递增输入指针.但循环是不必要的.您可以像这样一次复制所有字节:

std::memcpy(result, &number, sizeof number);
Run Code Online (Sandbox Code Playgroud)

要意识到字节的顺序取决于cpu 的字节顺序.


Dav*_*lor 8

首先,您希望转换是大端还是小端?以前的大多数答案将开始以相反的顺序为您提供字节,并在您切换架构后立即破坏您的程序。

如果您需要获得一致的结果,您可能希望将 64 位输入转换为大端(网络)字节顺序,或者可能是小端。例如,在 GNU glib 上,函数是GUINT64_TO_BE(),但对于大多数编译器都有一个等效的内置函数。

完成后,有几种选择:

使用 memcpy() 或 memmove() 复制

这是语言标准保证可以工作的方法,尽管在这里我使用了第三方库中的一个函数(在所有平台上将参数转换为大端字节序)。例如:

#include <stdint.h>
#include <stdlib.h>

#include <glib.h>

union eight_bytes {
  uint64_t u64;
  uint8_t b8[sizeof(uint64_t)];
};

eight_bytes u64_to_eight_bytes( const uint64_t input )
{
  eight_bytes result;
  const uint64_t big_endian = (uint64_t)GUINT64_TO_BE((guint64)input);

  memcpy( &result.b8, &big_endian, sizeof(big_endian) );
  return result;
}
Run Code Online (Sandbox Code Playgroud)

在带有 的 Linux x86_64 上clang++ -std=c++17 -O,这基本上编译为指令:

bswapq  %rdi
movq    %rdi, %rax
retq
Run Code Online (Sandbox Code Playgroud)

如果你想在所有平台上的little-endian顺序的结果,你可以更换GUINT64_TO_BE()使用GUINT64_TO_LE(),并删除第一个指令,然后声明函数inline删除第三指令。(或者,如果您确定跨平台兼容性无关紧要,您可能会冒着忽略规范化的风险。)

因此,在现代 64 位编译器上,此代码与其他任何代码一样高效。在另一个目标上,它可能不是。

打字

在 C 中编写 this 的常用方法是union像以前一样声明,设置其uint64_t成员,然后读取其uint8_t[8]成员。这在 C 中是合法的。

我个人喜欢它,因为它允许我将整个操作表示为静态单个赋值。

但是,在 C++ 中,它是形式上未定义的行为。在实践中,我知道的所有 C++ 编译器都支持普通旧数据(语言标准中的正式术语),大小相同,没有填充位,但不支持具有虚函数表和喜欢。在我看来,标准的未来版本更有可能在 POD 上正式支持类型双关,而不是任何重要的编译器都会默默地破坏它。

C++ 指南方式

Bjarne Stroustrup 建议,如果您要键入双关语而不是复制,请使用reinterpret_cast,例如

uint8_t (&array_of_bytes)[sizeof(uint64_t)] =
      *reinterpret_cast<uint8_t(*)[sizeof(uint64_t)]>(
        &proper_endian_uint64);
Run Code Online (Sandbox Code Playgroud)

他的理由是,明确的演员表和通过 a 的类型双关语union都是未定义的行为,但演员表公然明确无误地表明你是故意用脚射击自己,而阅读union与活跃成员不同的成员可能是一种非常微妙的错误。

  • 这应该是公认的答案!对于OP,请考虑接受以确保未来的问题读者能够看到它。 (2认同)

Ram*_*mes 5

如果我理解正确的话,你可以这样做,例如:

uint64_t number = 23425432542254234532;
uint8_t *p = (uint8_t *)&number;
//if you need a copy
uint8_t result[8];
for(int i = 0; i < 8; i++) {
    result[i] = p[i];
}
Run Code Online (Sandbox Code Playgroud)

  • 您在此处为类型别名“uint8_t *p = (uint8_t *)&amp;number”。严格的别名规则不允许这样做。 (5认同)
  • @Niall - 任何指针都可以转换为“unsigned char*”,而不会违反严格的别名。而 `uint8_t`,如果存在的话,将是 `unsigned char` 的 typedef。 (2认同)