【问题标题】:std::memcpy vs std::copy_n for legacy c structsstd::memcpy vs std::copy_n 用于遗留 c 结构
【发布时间】:2020-04-12 12:11:40
【问题描述】:

在处理遗留的C 代码时,我需要读取和复制 C 样式结构的内存位置。 给定一个指向结构的指针,我应该更好地使用 C 风格 std::memcpy 还是 C++ std::copy_n?或者它们是等价的?

有一个具体的,虽然微不足道的例子:

#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;
}

问题出现在序列化 C 结构的上下文中。在具体情况下,我不知道在 C 库中定义的 Ctype 的内容。

一些说明

该示例可能过于简化,显然可以简单地使用b=*buffer。然而,我想到的是buffer 可以从外部提供,例如它可以是从磁盘读取的数据序列。然后我需要主动将缓冲区复制到b中。

加法

这是一个更接近我的想法的例子。它使用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;
}

【问题讨论】:

  • 为什么不只是*buffer = b;?由于它是 C 风格的结构,因此您不必担心构造函数或析构函数。
  • 不知道Ctype的内容是什么意思?你的意思是它可能不是 double+int 或者你不知道它们的值?
  • @Barmar 我想到了从 outside 给定缓冲区的情况,并且您想通过显式复制内存内容来使用缓冲区初始化 b。
  • @ALX23z 我不知道它是 int、double 还是别的什么。我所知道的是,如果我包含 C 库,我就有这种类型的 Ctype,它是一个 C 结构。当然,我可以检查 C 代码,但这违背了将 C 库用作“用户”的想法。
  • @francesco 如果您可以使用sizeof(b),您必须有权访问结构定义。

标签: c++ c memory-management


【解决方案1】:

std::copy_n 是一个执行复制操作的模板函数。对于可平​​凡复制的类型(C 风格的结构都应该是可平凡复制的),它与 memcpy 相同 - 并且应该具有相同的性能。

另外,memcpystd::copy_n 不同,不适用于不可复制的类型。

【讨论】:

    【解决方案2】:

    第一个代码不正确,因为您尝试复制到不包含对象的存储中。 memcpy 和 copy_n 只能更新现有对象,它们不能在空存储中创建对象。一个简单的解决方法是编写 write auto buffer = new Ctype;(尽管如果必须使用动态分配,我建议使用容器进行内存管理)。

    解决了这个问题后,您的两种方法都等效于 C 风格的结构(即,其定义在 C 中语法上有效的一种)。这些是 C++ 中可简单复制类型的子集。因此,使用哪个是风格问题;我的偏好是写:

    b = *buffer;
    

    这似乎比函数调用语法更清晰。 memcpy 版本的一个问题是,在将来代码将结构更改为不再可轻松复制的情况下,它不会很健壮。

    【讨论】:

      猜你喜欢
      • 2011-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 2019-10-11
      • 2015-08-31
      • 1970-01-01
      • 2013-11-19
      相关资源
      最近更新 更多