将uint64_t转换为uint8_t [8]

时间:2016-02-02 12:03:54

标签: c++ boost

如何在不丢失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);
}

7 个答案:

答案 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(),但是大多数编译器都有一个等效的内置函数。

这样做,有几种选择:

使用memcpy()或memmove()复制

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

#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位编译器上,此代码与其他任何代码一样有效。在另一个目标上,可能不是。

Type-Punning

用C编写此代码的常用方法是像以前一样声明union,设置其uint64_t成员,然后读取其unsigned char[]成员。这在C语言中是合法的。

我个人喜欢它,因为它使我可以将整个操作表示为静态单项作业。

但是,在C ++中,它是形式上未定义的行为。实际上,我知道所有C ++编译器都支持相同大小,没有填充位的Plain Old Data(语言标准的正式术语),但不支持具有虚拟功能表和喜欢。在我看来,标准的未来版本将正式支持POD上的类型处理,而不是任何重要的编译器都将其默默破解。

C ++准则方式

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的兼容性。