这与this question非常相似,但答案并没有真正回答这个问题,所以我想我会再问:
有时我会与返回可变长度结构的函数进行交互;例如,Windows中的FSCTL_GET_RETRIEVAL_POINTERS
返回可变大小的RETRIEVAL_POINTERS_BUFFER
结构。
在C ++中不鼓励使用malloc
/ free
,所以我想知道:
在标准C ++中分配可变长度缓冲区的“正确”方法是什么(即没有Boost等)?
vector<char>
类型不安全(如果我理解的话,不保证对齐),new
不适用于自定义大小的分配,我想不出一个好的替代品。有什么想法吗?
答案 0 :(得分:5)
我会使用std::vector<char> buffer(n)
。在C ++中确实没有可变大小的结构,所以你必须伪造它;把窗户安全扔出窗外。
答案 1 :(得分:2)
我认为您无法使用std::vector<char>
:
{
std::vector<char> raii(memory_size);
char* memory = &raii[0];
//Now use `memory` wherever you want
//Maybe, you want to use placement new as:
A *pA = new (memory) A(/*...*/); //assume memory_size >= sizeof(A);
pA->fun();
pA->~A(); //call the destructor, once done!
}//<--- just remember, memory is deallocated here, automatically!
好的,我理解你的对齐问题。这并不复杂。你可以这样做:
A *pA = new (&memory[i]) A();
//choose `i` such that `&memory[i]` is multiple of four, or whatever alignment requires
//read the comments..
答案 2 :(得分:2)
如果您喜欢malloc()
/ free()
,可以使用
RETRIEVAL_POINTERS_BUFFER* ptr=new char [...appropriate size...];
... do stuff ...
delete[] ptr;
关于对齐的标准的引用(expr.new/10):
对于char和unsigned char的数组,它们之间存在差异 new-expression的结果和返回的地址 分配函数应是最严格的整数倍 任何对象大小的基本对齐要求(3.11) 不大于正在创建的数组的大小。 [ 注意: 因为假定分配函数返回指向存储的指针 适用于任何类型的基础对象 对齐,这种对数组分配开销的约束允许 分配字符数组的常用习惯用法 稍后将放置其他类型。 - 结束说明]
答案 3 :(得分:0)
您可以考虑使用内存池,并且在RETRIEVAL_POINTERS_BUFFER结构的特定情况下,根据其定义分配池内存量:
sizeof(DWORD) + sizeof(LARGE_INTEGER)
加
ExtentCount * sizeof(Extents)
(我相信你比我更熟悉这个数据结构 - 上面主要是针对你问题的未来读者)。
内存池归结为“分配一堆内存,然后使用您自己的快速分配器将这些内存分成小块”。 您可以build your own memory pool,但值得查看Boosts memory pool,这是一个纯标头(没有DLL!)库。请注意,我没有使用Boost内存池库,但你确实询问过Boost,所以我想我会提到它。
答案 4 :(得分:0)
std::vector<char>
很好。通常,您可以使用零大小参数调用低级c函数,因此您知道需要多少。然后解决对齐问题:只需分配超出需要的数量,并偏移开始指针:
假设您希望缓冲区与4个字节对齐,分配needed size + 4
并添加4 - ((&my_vect[0] - reinterpret_cast<char*>(0)) & 0x3)
。
然后使用请求的大小和偏移指针调用c函数。
答案 5 :(得分:0)
好的,让我们从头开始吧。返回可变长度缓冲区的理想方法是:
MyStruct my_func(int a) { MyStruct s; /* magic here */ return s; }
不幸的是,这不起作用,因为sizeof(MyStruct)是在编译时计算的。任何变长都不适合在编译时计算大小的缓冲区。值得注意的是,这种情况发生在c ++支持的每个变量或类型上,因为它们都支持sizeof。 C ++只有一件事可以处理缓冲区的运行时大小:
MyStruct *ptr = new MyStruct[count];
所以解决这个问题的任何事情都必然会使用new的数组版本。这包括std :: vector和之前提出的其他解决方案。请注意,像char数组的新位置这样的技巧与sizeof完全相同。可变长度缓冲区只需要堆和数组。如果你想留在c ++中,那么就无法绕过这个限制。此外,它需要不止一个对象!这个很重要。你不能用c ++制作变长对象。这是不可能的。
c ++提供的最接近可变长度对象的是“从类型跳转到类型”。每个对象不需要是相同的类型,您可以在运行时操作不同类型的对象。但是每个部分和每个完整对象仍然支持sizeof,它们的大小是在编译时确定的。程序员唯一要做的就是选择你使用的类型。
那么我们解决这个问题的方法是什么?你如何创建可变长度的对象? std :: string提供了答案。它需要在内部有多个字符,并使用数组替代方法进行堆分配。但这都是由stdlib处理的,程序员不需要关心。然后你将有一个操纵那些std :: strings的类。 std :: string可以做到,因为它实际上是2个独立的内存区域。 sizeof(std :: string)确实返回一个内存块,其大小可以在编译时计算。但实际的可变长度数据位于由new的数组版本分配的单独内存块中。
new的数组版本对它自己有一些限制。 sizeof(a[0])==sizeof(a[1])
等。首先分配一个数组,然后对几个不同类型的对象进行新的放置将绕过这个限制。