简体   繁体   中英

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. Given a pointer to the struct, should I better use the C-style std::memcpy or the 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. In the concrete case I do not know the contents of Ctype which is defined in a C library.

Some clarifications

The example is perhaps overly simplified, and clearly one could simply use 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. Then I need to actively copy buffer into 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.

#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. For trivially copyable types (C style structs should all be trivially copyable) it is the same as memcpy - and should have the same performance.

Also, memcpy is not suitable for non-copyable types unlike std::copy_n .

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 . An easy fix would be to write 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). These are a subset of trivially-copyable types in 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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