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

时间:2017-02-19 16:33:43

标签: c++ struct memcpy

如果缓冲区大小足够,在两个不同的结构上调用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 ++进行编译。

1 个答案:

答案 0 :(得分:4)

除了推迟到C标准之外,C ++标准没有指定memcpy的行为。 (也许是为了避免解决这样的问题!)。在C标准中,它被定义为等同于字符类型副本序列 1

因此将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
  5. 的子对象的值会发生什么变化

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

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

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

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

    所以,总而言之,我认为您的具体示例是正确的,假设为sizeof(A) >= sizeof(B)

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

相关问题