立即分配指针和指针对象
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
.
对齐的类型一起使用,则必须四处走动以确保正确对齐
如果您正在认真考虑这个问题,我会考虑重新审视您的整个数据结构的设计,看看是否有其他方法可以做到这一点。 (也许这种方式实际上是最好的方式,但先好好想想)。
如果我想减少 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
.
如果您正在认真考虑这个问题,我会考虑重新审视您的整个数据结构的设计,看看是否有其他方法可以做到这一点。 (也许这种方式实际上是最好的方式,但先好好想想)。