立即分配指针和指针对象

Allocate Pointer and pointee at once

如果我想减少 malloc()s(特别是如果数据很小并且经常分配)我想立即分配指针和指针。

如果您假设如下:

struct entry {
    size_t      buf_len;
    char        *buf;
    int         something;
};

我想按以下方式分配内存(这里不关心错误检查):

size_t buf_len  = 4;                // size of the buffer
struct entry *e = NULL;

e = malloc( sizeof(*e) + buf_len ); // allocate struct and buffer
e->buf_len  = buf_len;              // set buffer size
e->buf      = e + 1;       // the buffer lies behind the struct

这甚至可以扩展,以便一次分配整个数组。

您如何评价这种技术:

这样合理吗?如果可以使用,有什么想法可以设计一个接口吗?

我觉得还不错 - 不过请发表评论

或者您可以这样做 - 这很常见

struct entry {
    size_t      buf_len;
    int         something;
    char        buf;
};

即使结构本身变长。并做

size_t buf_len  = 4;                // size of the buffer
struct entry *e = NULL;
//  check that it packs right
e = malloc(sizeof(size_t) + sizeof(int) + buf_len ); // allocate struct and buffer
e->buf_len  = buf_len;              // set buffer size
...... later
printf(&e.buf);

对于初学者,行:

e->buf      = e + sizeof(*e);       // the buffer lies behind the struct

应该是:

e->buf      = e + 1;       // the buffer lies behind the struct

这是因为e + 1将等于结构末尾的地址。如您所见,它只会是结构中的字节数等于指针中的字节数。

而且,是的,这是合理的。但是,我更喜欢这种方法:

struct entry {
    size_t      buf_len;
    int         something;
    char        buf[1];
};

这样,您就不会弄乱指针。只需根据需要添加尽可能多的字节,它们就会增加 buf 数组的大小。

注意:我使用与此类似的方法编写了一个文本编辑器,但使用了 Microsoft c++ 扩展,允许我将最后一个成员声明为 char buf[]。所以它是一个空数组,正好和我分配的额外字节数一样长。

至于可移植性,我不知道有任何问题,只要通过适当调用 sizeof() 找到大小,就像在您的代码中一样。

关于可维护性、可扩展性和可读性,您当然应该将分配和取消分配包装在注释良好的函数中。致电...

entry *allocate_entry_with_buffer();
void deallocate_entry_with_buffer(entry **entry_with_buffer);

...不需要知道内存实际如何处理的实现细节。人们经常使用自定义分配器和内存池等奇怪的东西。

至于速度,这肯定比进行大量小分配要快。我曾经用类似的策略分配整个二维矩阵...

它应该可以工作,但实际上您使用的是无用的间接寻址指针。 Windows API(例如)对可变大小结构使用另一种方法:可变大小缓冲区位于结构的最后并声明为 char buf[1].

您的结构将变为:

struct entry {
    size_t      buf_len;
    int         something;
    char        buf[1];
};

分配是(仍然没有错误检查):

size_t buf_len  = 4;                // size of the buffer
struct entry *e;

e = malloc( sizeof(*e) + buf_len  - 1); // struct already has room for 1 char
e->buf_len  = buf_len;              // set buffer size

仅此而已 e.buf 保证是大小为 buf_len 的字符数组。

这种方式确保即使变量部分不是字符数组而是 int、long 或任何数组,对齐将由最后一个元素给出,该元素是适当类型和大小 1 的数组。

您可以使用灵活的数组成员代替指针:

struct entry {
    size_t      buf_len;
    int         something;
    char        buf[];
};

// ...
struct entry *e = malloc(sizeof *e  + buf_len);
e->buf_len = buf_len;

便携性和性能都很好。可读性:不完美但足够好。

可扩展性:您一次不能对多个成员使用它,您必须回退到显式指针版本。此外,显式指针版本意味着如果您将它与没有 1.

对齐的类型一起使用,则必须四处走动以确保正确对齐

如果您正在认真考虑这个问题,我会考虑重新审视您的整个数据结构的设计,看看是否有其他方法可以做到这一点。 (也许这种方式实际上是最好的方式,但先好好想想)。