现在我正在阅读关于对齐,aliasing, padding和placement的文档,但我仍然不确定如何解决这个问题-这是我第一次面对这个级别的内存问题,所以我没有信心。
问题是:我有一个缓冲区,一个数据数组,它使用某种类型typeT
,例如
typeT buff[N];
在这个缓冲区中,我必须在buff
的开头存储typeU
类型的其他数据。假设有typeU
类型的K
个元素,少于或等于typeT
类型的M
个元素。
编辑:这个问题取决于数据类型,但我的问题是通用的,并且适用于POD和非POD。但是您可以假设非POD只是POD类型的聚合,没有需要复制的动态内存,并且所有数据都包含在结构本身的sizeof(typeX)
字节中。也就是说,不用担心深/浅复制。
我对typeT
和typeU
有不同排列的情况最感兴趣。
问题1:(最重要的问题)考虑到我将总是使用typeU访问这些数据,就像下面的代码一样,总是安全地在buff
上存储我的数据吗?
typeU *allocData = new (buff) typeU[K];
allocData[0] = foo;
问题2:是否总是安全地从typeU*
中复制任何数据使用memcpy
类型的typeT*
缓冲区内,鉴于我将始终使用typeU*
访问数据,如以下代码?
typeU prevData[K];
memcpy(buff, prevData, sizeof(typeU) * K);
// Is it safe to do the following?
typeU *accessData = reinterpret_cast<typeU *>(buff);
accessData[1] = foo;
问题3:(最不重要的问题)是不是仅仅转换指针并使用它们写数据并不总是安全的?就像
typeU *castData = (typeU*)buff;
castData[2] = something;
即使我总是使用typeU
使用内存的第一个区域(前M个元素),使用typeT
使用第二个区域(从M到N的元素)?
另外,请考虑我正在谈论的是c++ 98或03,而不是11,具有最小的STL子集,没有boost,并不总是在x86上-但我可以使用memcpy和placement new,正如我所说的。
如果typeT
和typeU
的大小和对齐要求相同,您可以使用为typeT
分配的未初始化存储来保存typeU
的值。
如果typeT
和/或typeU
具有构造函数或析构函数,则必须确保以正确的顺序调用它们。
static_assert(sizeof(typeT) == sizeof(typeU))
static_assert(alignof(typeT) == alignof(typeU))
typeT t[10];
&t[0]->~typeT(); // placement delete t[0] to uninitialize
typeU* p = &t[0];
new (p) typeU(); // construct typeU at t[0]
typeU u& = *p;
u.doStuff();
p->~typeU(); // placement delete typeU at t[0]
new (&t[0]) typeT(); // reconstruct typeT at t[0]
我刚发现Sutter的这篇文章,里面介绍了一些我不知道的东西
任何通过new或malloc动态分配的内存都是保证对除缓冲区以外的任何类型的对象正确对齐不是动态分配的,没有这样的保证
这意味着,首先,像我那样做是错误的:
typeT buff[N];
new (buff) typeU[K];
因为buff不是动态分配的,所以不能保证。
所以,关键是:要做这种处理,内存必须动态分配。如果不是,则答案取决于对齐。