【发布时间】:2016-12-25 21:30:50
【问题描述】:
免责声明:这是试图深入研究一个更大的问题,所以请不要纠结这个例子在实践中是否有意义。
而且,是的,如果您想复制 对象,请使用/提供复制构造函数。 (但请注意,即使示例也不会复制整个对象;它会尝试在几个相邻的(Q.2)整数上 blit 一些内存。)
给定一个 C++ Standard Layout struct,我可以使用 memcpy 一次写入多个(相邻)子对象吗?
完整示例:(https://ideone.com/1lP2Gdhttps://ideone.com/YXspBk)
#include <vector>
#include <iostream>
#include <assert.h>
#include <inttypes.h>
#include <stddef.h>
#include <memory.h>
struct MyStandardLayout {
char mem_a;
int16_t num_1;
int32_t num_2;
int64_t num_3;
char mem_z;
MyStandardLayout()
: mem_a('a')
, num_1(1 + (1 << 14))
, num_2(1 + (1 << 30))
, num_3(1LL + (1LL << 62))
, mem_z('z')
{ }
void print() const {
std::cout <<
"MySL Obj: " <<
mem_a << " / " <<
num_1 << " / " <<
num_2 << " / " <<
num_3 << " / " <<
mem_z << "\n";
}
};
void ZeroInts(MyStandardLayout* pObj) {
const size_t first = offsetof(MyStandardLayout, num_1);
const size_t third = offsetof(MyStandardLayout, num_3);
std::cout << "ofs(1st) = " << first << "\n";
std::cout << "ofs(3rd) = " << third << "\n";
assert(third > first);
const size_t delta = third - first;
std::cout << "delta = " << delta << "\n";
const size_t sizeAll = delta + sizeof(MyStandardLayout::num_3);
std::cout << "sizeAll = " << sizeAll << "\n";
std::vector<char> buf( sizeAll, 0 );
memcpy(&pObj->num_1, &buf[0], sizeAll);
}
int main()
{
MyStandardLayout obj;
obj.print();
ZeroInts(&obj);
obj.print();
return 0;
}
鉴于C++ Standard中的措辞:
9.2 类成员
...
13 分配具有相同访问控制(第 11 条)的(非联合)类的非静态数据成员,以便以后的成员具有 类对象中的更高地址。 (...) 实施对齐要求可能会导致两个 相邻的成员不要紧挨着分配; (...)
我会得出结论,可以保证num_1 到num_3 具有增加的地址并且是相邻的模填充。
为了完全定义上述示例,我看到了这些要求,但我不确定它们是否满足:
-
memcpy必须被允许以这种方式一次写入多个“内存对象”,即明确- 使用目标地址
num_1和大于num_1“对象”大小的大小调用memcpy是合法的。 (鉴于num_1不是数组的一部分。)(Is memcpy(&a + 1, &b + 1, 0) defined in C11? 似乎是一个很好的相关问题,但不太合适。) -
C++ (14) Standard,AFAICT,将
memcpy的描述引用到C99 Standard,并指出:
7.21.2.1 memcpy 函数
2 memcpy函数从s2指向的对象中复制n个字符 进入s1所指向的对象。
所以对我来说这里的问题是 wrt。这就是我们这里的目标范围是否可以根据 C 或 C++ 标准被视为“对象”。注意:对于
memcpy而言,当然可以假设一个(部分)字符数组,声明和定义为“一个对象”,因为我很确定我允许从 char 数组的一部分复制到(另一个)char 数组的另一部分。所以那么的问题是,将三个成员的内存范围重新解释为“概念”(?)字符数组是否合法。
- 使用目标地址
计算
sizeAll是合法的,即offsetof的使用是合法的,如图所示。写入成员之间的填充是合法的。
这些属性是否成立?我还有什么遗漏吗?
【问题讨论】:
-
在这种情况下不要使用
memcpy()或std::copy()。改为提供复制构造函数。 -
@πάνταῥεῖ - 你知道,我确实包含了免责声明,并且问题包含 [lang-lawyer] 标签。这是 SW 专业人士的网站。我不需要包括“不要在家里做这个,孩子们”的标志,还是我? :-P
-
delta的计算是未定义的,因为指针不指向同一个数组。 (第 5.7 节第 6 段最后一句。) -
@MartinBa “这些属性是否成立?” 它从来都不适用于任何标准,这也没有改变。您的标记没有帮助。
-
@MartinBa 我已经提到了 C++11 标准。
标签: c++ language-lawyer memcpy