使用 memcpy() 复制到 std::chrono::milliseconds 会出现错误 -Werror=class-memaccess

Copying to std::chrono::milliseconds with memcpy() gives error -Werror=class-memaccess

对于此代码:

std::array<unsigned char, 6> myArray = {123, 123, 112, 0, 15};
std::chrono::milliseconds dest{0};
memcpy(&dest, &myArray, 5);

Gcc 给出以下错误:

warning: 'void* memcpy(void*, const void*, size_t)' copying an object of type 'std::chrono::milliseconds'
{aka 'struct std::chrono::duration<long int, std::ratio<1, 1000> >'}
with 'private' member 'std::chrono::duration<long int, std::ratio<1, 1000> >::__r'
from an array of 'struct std::array<unsigned char, 6>';
use assignment or copy-initialization instead [-Wclass-memaccess]

当我只需要从数组中复制 5 个字节而不是整个数组时,处理此问题的最佳方法是什么?

这是我的完整代码:

std::array<uint8_t, 6> myArray = {0};
uint32_t time = 500000000;

memcpy(&myArray, &time, 4);
std::chrono::milliseconds ret{0};

memcpy(&ret, &myArray, 4);

所以我试图在 ret 中获得与 time

中相同的值

你这样初始化持续时间:

std::chrono::milliseconds::rep r = 0;
// initialise r with your array
std::chrono::milliseconds dest{r};

现在,您需要了解如何初始化 r。您可以使用 memcpy,但是数组的格式和目标系统上的整数表示之间将存在依赖关系。换句话说,程序读取的数据将不会跨不兼容的系统兼容。

实现整数反序列化的可移植方法是指定数据格式——通常选择大端八位字节。一旦确定了输入的格式,就可以使用位移位、位掩码和按位或运算将各个八位位组组合成一个整数值。虽然这主要是直截了当的,但它有一些容易犯的错误(尤其是有符号整数),所以我建议使用预先存在的解决方案。标准库没有反序列化功能。


关于您的修改:

uint32_t time = 500000000;
memcpy(&myArray, &time, 5);

程序的行为未定义,因为您溢出了大小为 4 个字节或更少的 uint32_t 对象。您试图复制 5 个字节。

一个正确的程序:

// copy into the array
static_assert(sizeof(std::uint32_t) == 4);
std::memcpy(myArray.data(), &time, 4);

// copy back
std::uint32_t r;
std::memcpy(&r, myArray.data(), 4);
std::chrono::milliseconds dest{r};

请确保不要将该数组复制到系统外部,因为数据不可移植。

std::chrono::milliseconds 中的时间表示,除了比率类型(可能不是非静态成员)和时钟滴答中的周期长度外,它的表示没有定义它可能由任何东西表示,例如long long 或 std::intmax_t 字段(如果至少有 45 位长)。 std::chrono::milliseconds 是 class std::chrono::duration 的实例化,不是简单的可构造或可复制的。您不知道那些 classes 的布局,它未定义并且 std::chrono::milliseconds 对象的地址可能与数据开头的地址不匹配。所以几乎你所做的一切都是一个 UB。

处理这个问题的唯一正确方法是以定义的方式形成适当的算术值并通过定义的设施分配它。

不依赖于了解 std::chrono::duration 的内部布局的更安全的实现方式是复制到一个整数,然后将该整数传递给持续时间:

std::array<unsigned char, 6> myArray = {123, 123, 112, 0, 15};
int64_t milliseconds = 0;
memcpy(&milliseconds, &myArray, 5);
std::chrono::milliseconds dest{milliseconds};