如果缓冲区大小足够,在两个不同的结构上调用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 ++进行编译。
答案 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 ++标准来覆盖该代码。
现在的问题是:
&tmp
,&obj
是否真的给出了对象开头的地址?obj
填充字节怎么样?tmp
中未初始化的填充字节怎么样?obj
问题1:是的,这由[class.mem] / 19涵盖,因为没有基类子对象(并且它不会超载operator&
)。
问题2:我找不到任何专门涵盖此内容的文字;但是,如果不允许写填充字节,则将类型对象复制到char缓冲区并返回到对象的标准中的示例将不起作用。
问题3:在[dcl.init] / 12中有一些文本明确允许将上述代码用于未初始化的数据;目的地将包含不确定的值。因此,如果源中未初始化的填充字节仅映射到目标中未初始化的填充字节,那就没问题。但是如果它们映射到目标中的子对象,那么这些对象将具有不确定的值。
问题4:这里没有问题,严格的别名规则允许对象通过字符类型表达式覆盖一些(或全部)字节。稍后访问该对象将产生与该表示相对应的值,如果它不表示值,则使用UB。
所以,总而言之,我认为您的具体示例是正确的,假设为sizeof(A) >= sizeof(B)
。
1 在C中,memcpy还会保留对象的有效类型。 C ++有一个不同的对象模型,没有相应的。因此,如果您使用类似的代码与C编译器,您还需要遵守两个对象中的类型之间的严格别名规则。