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++ 标准覆盖该代码。
现在的问题是:
&tmp
,&obj
实际上给出了对象的开始地址吗?
obj
中的填充字节如何?
tmp
中未初始化的填充字节怎么办?
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 编译器中使用类似的代码,您还需要遵守两个对象中类型之间的严格别名规则。
如果缓冲区足够大,在两个不同的结构上调用 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++ 标准覆盖该代码。
现在的问题是:
&tmp
,&obj
实际上给出了对象的开始地址吗?obj
中的填充字节如何?tmp
中未初始化的填充字节怎么办?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 编译器中使用类似的代码,您还需要遵守两个对象中类型之间的严格别名规则。