【问题标题】:Does memcpy preserve data between different types?memcpy 是否保留不同类型之间的数据?
【发布时间】:2017-02-19 16:33:43
【问题描述】:

如果缓冲区大小足够,在两个不同的结构上调用memcpy 是否会保留原始数据?如果它们各自的数据类型重叠,是否定义为使用先前数据类型的数据检索另一种数据类型的值?

这对于两种 c/cpp 语言应该是相似的,但我在 cpp 中提供了一个示例 -

#include <iostream>
#include <cstring>

using namespace std;

struct A{
  int a;
  char b[10];
};

struct B{
  int ba;
  int bb;
};

int main(){
    B tmp;
    tmp.ba = 50;
    tmp.bb = 24;
    cout << tmp.ba << tmp.bb << "\n";

    // everything is fine yet

    A obj;
    memcpy(&obj, &tmp, sizeof(tmp));

    // 1. is this valid?
    cout << obj.a << "\n";

    B newB;
    memcpy(&newB, &obj, sizeof(newB));

    // 2. Are these valid?
    cout << newB.ba << newB.bb << "\n";
}

在上面的示例中,我已经评论了第 1 条和第 2 条评论,它们是否有效并且如果提供了足够的缓冲区,数据是否会保留?我们可以便携吗?

结构和其他相关函数在 C 库中,但我们将使用 c++ 进行编译。

【问题讨论】:

  • memcpy 既不知道也不关心数据类型。它复制您指定的字节数,无论目标缓冲区是否足够大。当范围重叠时,行为是 undefined 并且应该使用 memmove
  • memcpy() 对底层数据类型不做任何保证。
  • C 还是 C++?这是两种不同的语言,具有完全不同的对象模型。
  • @BaummitAugen 不同意 memcpy 结束 obj 的生命周期
  • @user6241591 要从 C++ 程序中使用 C 库,您应该使用 C 编译器编译库,并使用链接器组合部件

标签: c++ struct memcpy


【解决方案1】:

C++ 标准没有指定memcpy 的行为,只是遵从 C 标准。 (也许是为了避免处理这样的问题!)。在 C 标准中,它被定义为等同于一系列字符类型的副本1

因此,将memcpy(&amp;obj, &amp;tmp, sizeof(tmp)); 视为:

unsigned char *dst = (char *)&obj;
unsigned char *src = (char *)&tmp;
for (size_t i = 0; i != sizeof tmp; ++i)
    dst[i] = src[i];

然后使用 C++ 标准来覆盖该代码。

现在的问题是:

  1. &amp;tmp&amp;obj真的给出了对象的起始地址吗?
  2. obj 中的填充字节怎么样?
  3. tmp 中未初始化的填充字节怎么办?
  4. obj 的子对象的值会发生什么变化?

问题 1:是的,这在 [class.mem]/19 中涵盖,因为没有基类子对象(并且它不会重载 operator&amp;)。

问题 2:我找不到任何专门涉及此问题的文本;但是,如果不允许写入填充字节,则将类类型对象复制到 char 缓冲区并返回到对象的标准中的示例将不起作用。

问题 3:在 [dcl.init]/12 中有一些文本明确允许将上述代码用于未初始化的数据;并且目的地将包含不确定的值。因此,如果源中未初始化的填充字节仅映射到目标中未初始化的填充字节,那很好。但是如果它们被映射到目标中的子对象,那么这些对象将具有不确定的值。

问题 4:这里没有问题,严格的别名规则允许对象通过字符类型表达式覆盖其部分(或全部)字节。稍后访问该对象将产生与表示对应的值,如果它不表示值,则使用 UB。

所以,总而言之,我认为您的具体示例是可以的,假设 sizeof(A) &gt;= sizeof(B)


1 在 C 中,memcpy 还保留了对象的有效类型。 C++ 有一个不同的对象模型,没有与之等效的对象模型。因此,如果您在 C 编译器中使用类似的代码,您还需要遵守两个对象中类型之间的严格别名规则。

【讨论】:

  • 关于第三点,这也应该支持你的论点-en.cppreference.com/w/cpp/language/data_members#Standard_layout,对吧?
  • @M.M 如果我在调用memcpy() 之前memset(..,0,..) 的所有结构@ 这应该解决未初始化位问题?
  • @user6241591 可以。
  • @M.M 这是否意味着在 [basic.types]/3 中用于临时缓冲区而不是 char buf[N] 可以使用 int buf[...]double buf[...] 或几乎任何分配的内存,假设足够尺寸? (只有当我们稍后尝试访问混乱且不确定的buf 数据时,UB 才可能发生。)
  • @AlexD 我想是的。可能有一些原因导致无法使用非标准布局类型,但我不确定相关的标准条款是什么
猜你喜欢
  • 1970-01-01
  • 2016-06-24
  • 2015-12-12
  • 2018-05-12
  • 2022-12-15
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 2012-04-18
相关资源
最近更新 更多