memcpy 是否保留不同类型之间的数据?

Does memcpy preserve data between different types?

如果缓冲区足够大,在两个不同的结构上调用 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";
}

在上面的例子中,我评论了第一条和第二条评论,它们是否有效,如果提供足够的缓冲区,是否会保留数据?我们可以便携地做到这一点吗?

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

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

所以把memcpy(&obj, &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. &tmp,&obj实际上给出了对象的开始地址吗?
  2. obj 中的填充字节如何?
  3. tmp 中未初始化的填充字节怎么办?
  4. obj 的 sub-objects 的值发生了什么变化?

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

问题 2:我找不到任何专门介绍这个的文字;但是标准中将 class-type 对象复制到 char 缓冲区并返回到对象的示例,如果不允许写入填充字节,则将无法工作。

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

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

所以,总而言之,假设 sizeof(A) >= sizeof(B)

,我认为你的具体示例是可以的

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