如何uint64_t在uint8_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)
你快到了.首先,文字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)
首先,您希望转换是大端还是小端?以前的大多数答案将开始以相反的顺序为您提供字节,并在您切换架构后立即破坏您的程序。
如果您需要获得一致的结果,您可能希望将 64 位输入转换为大端(网络)字节顺序,或者可能是小端。例如,在 GNU glib 上,函数是GUINT64_TO_BE(),但对于大多数编译器都有一个等效的内置函数。
完成后,有几种选择:
这是语言标准保证可以工作的方法,尽管在这里我使用了第三方库中的一个函数(在所有平台上将参数转换为大端字节序)。例如:
#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 上正式支持类型双关,而不是任何重要的编译器都会默默地破坏它。
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与活跃成员不同的成员可能是一种非常微妙的错误。
如果我理解正确的话,你可以这样做,例如:
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)
| 归档时间: |
|
| 查看次数: |
8935 次 |
| 最近记录: |