简体   繁体   English

遗留 c 结构的 std::memcpy 与 std::copy_n

[英]std::memcpy vs std::copy_n for legacy c structs

In dealing with legacy C code, I need to read and copy the memory location of a C-style struct.在处理遗留C代码时,我需要读取和复制 C 样式结构的内存位置。 Given a pointer to the struct, should I better use the C-style std::memcpy or the C++ std::copy_n ?给定一个指向结构的指针,我应该更好地使用 C 风格的std::memcpy还是 C++ std::copy_n Or are they equivalent?或者它们是等价的?

To have a concrete, albeit trivial, example:有一个具体的,虽然微不足道的例子:

#include <cstring>
#include <algorithm>

struct Ctype {
   double x;
   int a;
};

int main()
{
   Ctype a{1, 2};
   auto p = &a;
   auto buffer = reinterpret_cast<decltype(p)>(::operator new(sizeof(a)));


   // Are the two following equivalent and both well-defined?
   std::memcpy(buffer, p, sizeof(a));
   std::copy_n(p, 1, buffer);

   Ctype b{3, 4};
   // Are the two following equivalent and both well-defined?
   std::memcpy(&b, buffer, sizeof(b));
   std::copy_n(buffer, 1, &b);

   delete buffer;
   return 0;
}

The question arises in the context of serializing a C struct.问题出现在序列化 C 结构的上下文中。 In the concrete case I do not know the contents of Ctype which is defined in a C library.在具体情况下,我不知道 C 库中定义的Ctype的内容。

Some clarifications一些说明

The example is perhaps overly simplified, and clearly one could simply use b=*buffer .该示例可能过于简化,显然可以简单地使用b=*buffer What I have in mind, however, is that buffer could be provided from outside, for example it could be a sequence of data read from the disk.然而,我想到的是buffer可以从外部提供,例如它可以是从磁盘读取的数据序列。 Then I need to actively copy buffer into b.然后我需要主动将缓冲区复制到 b 中。

Addition添加

This is an example closer to what I have in mind.这是一个更接近我的想法的例子。 It uses the zlib library to write/read from the disk: the zlib functions gzread and gzwrite accept a void * pointer to a buffer of memory to read/write.它使用zlib库从磁盘写入/读取:zlib 函数gzreadgzwrite接受指向要读取/写入的内存缓冲区的void *指针。

#include <cstring>
#include <algorithm>
#include <fstream>

extern "C" {
#include <zlib.h>
}

struct Ctype {
   double x;
   int a;
};

int main()
{
   Ctype a{1, 2};
   auto p = &a;
   auto buffer = ::operator new(sizeof(a));

   // Are the two following equivalent and both well-defined?
   std::memcpy(buffer, p, sizeof(a));
   std::copy_n(p, 1, static_cast<Ctype *>(buffer));

   // Saving the files
   gzFile f = gzopen("conf.dat", "w");
   gzwrite(f, buffer, sizeof(a));
   gzclose(f);

   Ctype b{3, 4};
   // Reading file
   f = gzopen("conf.dat", "r");
   gzread(f, buffer, sizeof(a));
   gzclose(f);
   // Are the two following equivalent and both well-defined?
   std::memcpy(&b, buffer, sizeof(b));
   std::copy_n(static_cast<Ctype *>(buffer), 1, &b);

   operator delete(buffer);
   return 0;
}

std::copy_n is a template function that performs copy operations. std::copy_n是一个执行复制操作的模板函数。 For trivially copyable types (C style structs should all be trivially copyable) it is the same as memcpy - and should have the same performance.对于可简单复制的类型(C 风格的结构都应该是可简单复制的),它与memcpy相同 - 并且应该具有相同的性能。

Also, memcpy is not suitable for non-copyable types unlike std::copy_n .此外,与std::copy_n不同, memcpy不适合不可复制的类型。

The first code is incorrect as you attempt to copy into storage that contains no objects.当您尝试复制到不包含对象的存储时,第一个代码不正确。 memcpy and copy_n can only update existing objects, they cannot create objects in vacant storage . memcpy 和 copy_n 只能更新现有对象,它们不能在空存储中创建对象。 An easy fix would be to write write auto buffer = new Ctype;一个简单的解决方法是编写 write auto buffer = new Ctype; (although I would suggest using a container for for memory management if you must use dynamic allocation). (尽管如果您必须使用动态分配,我会建议使用容器进行内存管理)。

Having fixed that, your two methods are both equivalent for a C-style struct (ie one whose definition is syntactically valid in C).解决了这个问题后,您的两种方法对于 C 风格的结构都是等效的(即,其定义在 C 中在语法上是有效的)。 These are a subset of trivially-copyable types in C++.这些是 C++ 中可简单复制的类型的子集。 So it is a matter of style as to which to use;因此,使用哪个是风格问题; my preference would be to write:我更喜欢写:

b = *buffer;

which seems clearer than a function call syntax .这似乎比函数调用语法更清晰。 A problem with the memcpy version is that it would not be robust in the case of future code changing the struct to no longer be trivially copyable. memcpy 版本的一个问题是,在未来代码将结构更改为不再可简单复制的情况下,它不会健壮。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM