什么是“正确的”?在 C++ 中分配可变大小缓冲区的方法?

发布于 2024-12-04 14:53:12 字数 624 浏览 1 评论 0原文

这与这个问题非常相似,但答案并没有真正回答这个问题,所以我我想我会再问一次:

有时我与返回可变长度结构的函数交互;例如,FSCTL_GET_RETRIEVAL_POINTERS Windows 中的 返回一个大小可变的 RETRIEVAL_POINTERS_BUFFER 结构。

在 C++ 中不鼓励使用 malloc/free,所以我想知道:
在标准 C++ 中分配可变长度缓冲区的“正确”方法是什么(即没有 Boost 等)?

vector 是类型不安全的(并且不保证任何有关对齐的内容,如果我理解正确的话),new 不起作用使用自定义大小的分配,我想不出一个好的替代品。有什么想法吗?

This is very similar to this question, but the answers don't really answer this, so I thought I'd ask again:

Sometimes I interact with functions that return variable-length structures; for example, FSCTL_GET_RETRIEVAL_POINTERS in Windows returns a variably-sized RETRIEVAL_POINTERS_BUFFER structure.

Using malloc/free is discouraged in C++, and so I was wondering:
What is the "proper" way to allocate variable-length buffers in standard C++ (i.e. no Boost, etc.)?

vector<char> is type-unsafe (and doesn't guarantee anything about alignment, if I understand correctly), new doesn't work with custom-sized allocations, and I can't think of a good substitute. Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不美如何 2024-12-11 14:53:12

我会使用 std::vector缓冲区(n)。 C++ 中确实不存在可变大小的结构这样的东西,所以你必须伪造它;将类型安全抛到窗外。

I would use std::vector<char> buffer(n). There's really no such thing as a variably sized structure in C++, so you have to fake it; throw type safety out the window.

蓝眼睛不忧郁 2024-12-11 14:53:12

如果您喜欢 malloc()/free(),您可以使用

RETRIEVAL_POINTERS_BUFFER* ptr=new char [...appropriate size...];

... do stuff ...

delete[] ptr;

有关对齐的标准中的引用 (expr.new/10):

对于 char 和 unsigned char 数组,它们之间的区别
new-表达式的结果和返回的地址
分配函数应为最严格分配函数的整数倍
其大小的任何对象类型的基本对齐要求(3.11)
不大于正在创建的数组的大小。 [ 笔记:
因为假设分配函数返回指向存储的指针
对于具有基本功能的任何类型的对象都适当对齐
对齐,对数组分配开销的这种限制允许
将字符数组分配到其中的对象的常见习惯用法
其他类型稍后会放置。 ——尾注]

If you like malloc()/free(), you can use

RETRIEVAL_POINTERS_BUFFER* ptr=new char [...appropriate size...];

... do stuff ...

delete[] ptr;

Quotation from the standard regarding alignment (expr.new/10):

For arrays of char and unsigned char, the difference between the
result of the new-expression and the address returned by the
allocation function shall be an integral multiple of the strictest
fundamental alignment requirement (3.11) of any object type whose size
is no greater than the size of the array being created. [ Note:
Because allocation functions are assumed to return pointers to storage
that is appropriately aligned for objects of any type with fundamental
alignment, this constraint on array allocation overhead permits the
common idiom of allocating character arrays into which objects of
other types will later be placed. — end note ]

我不会写诗 2024-12-11 14:53:12

我不明白为什么你不能使用 std::vector

{
   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..

I don't see any reason why you can't use 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!

Alright, I understand your alignment problem. It's not that complicated. You can do this:

A *pA = new (&memory[i]) A();
//choose `i` such that `&memory[i]` is multiple of four, or whatever alignment requires
//read the comments..
雨轻弹 2024-12-11 14:53:12

您可以考虑使用内存池,并且在 RETRIEVAL_POINTERS_BUFFER 结构的特定情况下,根据其定义分配池内存量:

sizeof(DWORD) + sizeof(LARGE_INTEGER)

加上

ExtentCount * sizeof(Extents)

(我相信您比我更熟悉这个数据结构 - 上面的内容主要是供您问题的未来读者使用)。

内存池归结为“分配一堆内存,然后使用您自己的快速分配器将该内存分成小块”。
您可以构建自己的内存池,但可能值得一看提升内存池,这是一个纯标头(没有DLL!) 图书馆。请注意,我没有使用 Boost 内存池库,但您确实询问了 Boost,所以我想我应该提到它。

You may consider using a memory pool and, in the specific case of the RETRIEVAL_POINTERS_BUFFER structure, allocate pool memory amounts in accordance with its definition:

sizeof(DWORD) + sizeof(LARGE_INTEGER)

plus

ExtentCount * sizeof(Extents)

(I am sure you are more familiar with this data structure than I am -- the above is mostly for future readers of your question).

A memory pool boils down to "allocate a bunch of memory, then allocate that memory in small pieces using your own fast allocator".
You can build your own memory pool, but it may be worth looking at Boosts memory pool, which is a pure header (no DLLs!) library. Please note that I have not used the Boost memory pool library, but you did ask about Boost so I thought I'd mention it.

青衫负雪 2024-12-11 14:53:12

std :: vector&lt; char&gt;很好。通常,您可以使用零尺寸的参数调用低级C-功能,以便您知道需要多少。然后,您解决了对齐问题:分配比您需要的更多,并抵消了开始指针:

说您要对缓冲区对齐到4个字节,分配所需尺寸 + 4,并添加4-(( (&amp; my_vect [0] - reinterpret_cast&lt; char*&gt;(0))&amp; 0x3)

然后用请求的大小和偏置指针调用您的C功能。

std::vector<char> is just fine. Typically you can call your low-level c-function with a zero-size argument, so you know how much is needed. Then you solve your alignment problem: just allocate more than you need, and offset the start pointer:

Say you want the buffer aligned to 4 bytes, allocate needed size + 4 and add 4 - ((&my_vect[0] - reinterpret_cast<char*>(0)) & 0x3).

Then call your c-function with the requested size and the offsetted pointer.

旧人哭 2024-12-11 14:53:12

好吧,让我们从头开始。返回可变长度缓冲区的理想方法是:

MyStruct my_func(int a) { MyStruct s; /* magic here */ return s; }

不幸的是,这不起作用,因为 sizeof(MyStruct) 是在编译时计算的。任何可变长度的内容都不适合在其大小在编译时计算的缓冲区内。值得注意的是,c++ 支持的每个变量或类型都会发生这种情况,因为它们都支持 sizeof。 C++ 只有一件事可以处理缓冲区的运行时大小:

MyStruct *ptr = new MyStruct[count];

因此任何要解决此问题的方法都必须使用 new 的数组版本。这包括 std::vector 和之前提出的其他解决方案。请注意,像将 new 放置到 char 数组这样的技巧与 sizeof 具有完全相同的问题。可变长度缓冲区只需要堆和数组。如果你想留在 c++ 中,就没有办法绕过这个限制。此外,它需要多个对象!这很重要。你不能用c++创建可变长度对象。这是不可能的。

C++ 提供的最接近可变长度对象的方法是“从类型跳转到类型”。每个对象不需要具有相同的类型,并且您可以在运行时操作不同类型的对象。但是每个部分和每个完整对象仍然支持 sizeof 并且它们的大小是在编译时确定的。程序员唯一要做的就是选择你使用的类型。

那么我们解决这个问题的办法是什么呢?如何创建可变长度对象? std::string 提供了答案。它内部需要有多个字符,并使用数组替代方式进行堆分配。但这都是由stdlib处理的,程序员不需要关心。然后您将拥有一个操作这些 std::string 的类。 std::string 可以做到这一点,因为它实际上是 2 个独立的内存区域。 sizeof(std::string) 确实返回一个内存块,其大小可以在编译时计算。但实际的变长数据位于由数组版本 new 分配的单独内存块中。

new 的数组版本本身有一些限制。 sizeof(a[0])==sizeof(a[1]) 等等。首先分配一个数组,然后对多个不同类型的对象进行放置 new 将绕过这个限制。

Ok, lets start from the beginning. Ideal way to return variable-length buffer would be:

MyStruct my_func(int a) { MyStruct s; /* magic here */ return s; }

Unfortunately, this does not work since sizeof(MyStruct) is calculated on compile-time. Anything variable-length just do not fit inside a buffer whose size is calculated on compile-time. The thing to notice that this happens with every variable or type supported by c++, since they all support sizeof. C++ has just one thing that can handle runtime sizes of buffers:

MyStruct *ptr = new MyStruct[count];

So anything that is going to solve this problem is necessarily going to use the array version of new. This includes std::vector and other solutions proposed earlier. Notice that tricks like the placement new to a char array has exactly the same problem with sizeof. Variable-length buffers just needs heap and arrays. There is no way around that restriction, if you want to stay within c++. Further it requires more than one object! This is important. You cannot make variable-length object with c++. It's just impossible.

The nearest one to variable-length object that the c++ provides is "jumping from type to type". Each and every object does not need to be of same type, and you can on runtime manipulate objects of different types. But each part and each complete object still supports sizeof and their sizes are determined on compile-time. Only thing left for programmer is to choose which type you use.

So what's our solution to the problem? How do you create variable-length objects? std::string provides the answer. It needs to have more than one character inside and use the array alternative for heap allocation. But this is all handled by the stdlib and programmer do not need to care. Then you'll have a class that manipulates those std::strings. std::string can do it because it's actually 2 separate memory areas. The sizeof(std::string) does return a memory block whose size can be calculated on compile-time. But the actual variable-length data is in separate memory block allocated by the array version of new.

The array version of new has some restrictions on it's own. sizeof(a[0])==sizeof(a[1]) etc. First allocating an array, and then doing placement new for several objects of different types will go around this limitation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文