如何在不丢失C ++信息的情况下将uint64_t
转换为uint8_t[8]
?
我尝试了以下内容:
uint64_t number = 23425432542254234532;
uint8_t result[8];
for(int i = 0; i < 8; i++) {
std::memcpy(result[i], number, 1);
}
答案 0 :(得分:7)
你快到了。首先,文字23425432542254234532
太大而不适合uint64_t
。
其次,正如您从文档中看到的那样,std::memcpy
具有以下声明:
void * memcpy ( void * destination, const void * source, size_t num );
如您所见,它将指针(地址)作为参数。不是uint64_t
,也不是uint8_t
。您可以使用address-of运算符轻松获取整数的地址。
很明显,你只是将整数的第一个字节复制到每个数组元素中。您需要在每次迭代中递增输入指针。但循环是不必要的。您可以像这样一次复制所有字节:
std::memcpy(result, &number, sizeof number);
确实知道字节的顺序取决于cpu的endianness。
答案 1 :(得分:3)
如果我理解正确,你可以这样做:
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];
}
答案 2 :(得分:2)
要么使用union,要么使用按位运算 - memcpy用于内存块,可能不是最佳选择。
uint64_t number = 23425432542254234532;
uint8_t result[8];
for(int i = 0; i < 8; i++) {
result[i] = uint8_t((number >> 8*(7 - i)) & 0xFF);
}
或者,虽然我告诉这会破坏规则,但它适用于我的编译器:
union
{
uint64_t a;
uint8_t b[8];
};
a = 23425432542254234532;
//Can now read off the value of b
uint8_t copy[8];
for(int i = 0; i < 8; i++)
{
copy[i]= b[i];
}
答案 3 :(得分:2)
在不兼容类型之间复制内存时,首先要注意的是严格别名 - 您不希望错误地指定别名指针。也需要考虑调整。
你几乎就在那里,不需要for
。
uint64_t number = 0x2342543254225423; // trimmed to fit
uint8_t result[sizeof(number)];
std::memcpy(result, &number, sizeof(number));
注意:也要注意平台的字节顺序。
答案 4 :(得分:0)
可以使用口罩包装和拆箱。还要担心的一件事是字节顺序。包装和拆包应使用相同的字节顺序。请注意-这不是超高效的实现,并且对于非本机64位的小型CPU也没有保证。
void unpack_uint64(uint64_t number, uint8_t *result) {
result[0] = number & 0x00000000000000FF ; number = number >> 8 ;
result[1] = number & 0x00000000000000FF ; number = number >> 8 ;
result[2] = number & 0x00000000000000FF ; number = number >> 8 ;
result[3] = number & 0x00000000000000FF ; number = number >> 8 ;
result[4] = number & 0x00000000000000FF ; number = number >> 8 ;
result[5] = number & 0x00000000000000FF ; number = number >> 8 ;
result[6] = number & 0x00000000000000FF ; number = number >> 8 ;
result[7] = number & 0x00000000000000FF ;
}
uint64_t pack_uint64(uint8_t *buffer) {
uint64_t value ;
value = buffer[7] ;
value = (value << 8 ) + buffer[6] ;
value = (value << 8 ) + buffer[5] ;
value = (value << 8 ) + buffer[4] ;
value = (value << 8 ) + buffer[3] ;
value = (value << 8 ) + buffer[2] ;
value = (value << 8 ) + buffer[1] ;
value = (value << 8 ) + buffer[0] ;
return value ;
}
答案 5 :(得分:0)
首先,您希望转换是大端还是小端?切换架构后,以前的大多数答案都会突然开始为您提供字节0x23
而不是0x32
。
如果需要获得一致的结果,则需要将64位输入转换为大端(网络)字节顺序,或者转换为小端。例如,在GNU glib上,该函数为GUINT64_TO_BE()
,但是大多数编译器都有一个等效的内置函数。
这样做,有几种选择:
这是语言标准保证的方法,尽管我在这里使用了第三方库中的一个函数(在所有平台上将参数转换为大端字节序)。例如:
#include <stdint.h>
#include <stdlib.h>
#include <glib.h>
union eight_bytes {
uint64_t u64;
unsigned char 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;
}
在具有clang++ -std=c++17 -O
的Linux x86_64上,这基本上可以编译为以下指令:
bswapq %rdi
movq %rdi, %rax
retq
如果您希望所有平台上的结果按低位序排列,则可以将GUINT64_TO_BE()
替换为GUINT64_TO_LE()
并删除第一条指令,然后声明函数inline
删除第三条指令指令。 (或者,如果您确定,跨平台兼容性无关紧要,则可能要冒忽略标准化的风险。)
因此,在现代的64位编译器上,此代码与其他任何代码一样有效。在另一个目标上,可能不是。
用C编写此代码的常用方法是像以前一样声明union
,设置其uint64_t
成员,然后读取其unsigned char[]
成员。这在C语言中是合法的。
我个人喜欢它,因为它使我可以将整个操作表示为静态单项作业。
但是,在C ++中,它是形式上未定义的行为。实际上,我知道所有C ++编译器都支持相同大小,没有填充位的Plain Old Data(语言标准的正式术语),但不支持具有虚拟功能表和喜欢。在我看来,标准的未来版本将正式支持POD上的类型处理,而不是任何重要的编译器都将其默默破解。
Bjarne Stroustrup建议,如果要键入双关语而不是复制,请使用repinterpret_cast
,例如
uint8_t (&array_of_bytes)[sizeof(uint64_t)] =
*reinterpret_cast<uint8_t(*)[sizeof(uint64_t)]>(
&proper_endian_uint64);
他的推理是,通过union
进行的显式强制转换和类型调整都是未定义的行为,但是强制转换使您无意间毫无误解地故意朝自己的脚开枪,而阅读另一种{ {1}}成员比活跃成员可能是一个非常微妙的错误。
答案 6 :(得分:-1)
#include<cstdint>
#include<iostream>
struct ByteArray
{
uint8_t b[8] = { 0,0,0,0,0,0,0,0 };
};
ByteArray split(uint64_t x)
{
ByteArray pack;
const uint8_t MASK = 0xFF;
for (auto i = 0; i < 7; ++i)
{
pack.b[i] = x & MASK;
x = x >> 8;
}
return pack;
}
int main()
{
uint64_t val_64 = UINT64_MAX;
auto pack = split(val_64);
for (auto i = 0; i < 7; ++i)
{
std::cout << (uint32_t)pack.b[i] << std::endl;
}
system("Pause");
return 0;
}
虽然union
处理的Straw1239
方法更好,更清晰。请注意编译器/平台与endianness
的兼容性。