C 中用于内存管理器的各种长度结构?
Various length structure in C for memory manager?
我练习用C实现一个内存管理器
我想要具有各种长度和自我描述的结构。
所以,我偷看了一本POSIX教科书的东西,像这样:
struct layout
{
uint32_t size; // array size in bytes, include space after the struct
uchar_t data[1];
};
// But, is next line correct?
layout *val = malloc (array_memory_in_bytes + sizeof (uint32_t) - 1);
// Where does a static array keep the pointer for using it?
如果我在不间断的内存中一个接一个地拥有多个这样的结构,并且我希望能够遍历它们。我可以写点像这样的东西吗:
layout *val1 = pointer;
layout *val2 = val1 + val1.size + sizeof (val1.size);
或者你能推荐我一个更好的方法吗?
它的标准 C 版本称为 灵活数组成员,它看起来像:
struct layout
{
uint32_t size;
uchar_t data[];
};
// allocate one of these blocks (in a function)
struct layout *val = malloc( sizeof *val + number_of_bytes );
val->size = number_of_bytes;
代码 val1->data + val1->size
将为您提供 space 您刚刚 malloc
的最后一个指针。
但是,您不能迭代一个 malloc
' 块的末尾并希望击中另一个 malloc
' 个块。要实现这个想法,您必须 malloc
一个大块,然后在其中放置各种 struct layout
对象,注意 alignment.
在这种方法中,最好还存储每个 struct layout
所在位置的索引。从理论上讲,您可以每次都从头开始浏览列表,添加 size
然后进行对齐调整;但这会很慢,而且这意味着你无法处理中间的块被释放并重新 "allocated".
如果这是 malloc
的直接替代品,那么实际上有两个对齐注意事项:
- 对齐
struct layout
data
必须对齐任何可能的类型
解决这个问题的最简单方法是也为任何可能的类型对齐 struct layout
。这可能看起来像(注意:需要 #include <stdint.h>
):
struct layout
{
uint64_t size; // may as well use 64 bits since they're there
_Alignas(max_align_t) uchar_t data[];
};
另一种方法可能是将 size
保持在 32 位并加入 pragma pack
以防止填充;那么你需要使用一些额外的复杂性来确保 struct layout
被放置在 max_align_t
字节边界之前 4 个字节,等等。我建议先用简单的方法来获取代码 运行;然后稍后您可以返回并尝试此更改,以便根据需要节省几个字节的内存。
替代方法:
- 将
struct layout
的每个实例及其尾随数据保存在单独的分配中。
- 将
data
更改为指向 malloc
的指针 space;那么您可以将所有 struct layout
对象保存在一个数组中。
一般的想法可行,但只有当 most-severe 边界对齐情况是 int 时,该特定结构才可行。
内存管理器,尤其是可能是 back-end 实现 malloc()
的内存管理器,必须知道 worst-case 边界是什么。数据的实际开始必须在该边界上,以满足分配的内存适当对齐以存储任何数据类型的一般要求。
完成此操作的最简单方法是使 layout
结构描述的长度分配 header 和实际分配大小都是该对齐单元的倍数。
无论如何,您不能将数据的开头描述为结构成员,并且该结构的大小就是 header 的大小。 C 不支持 zero-length 字段。您应该使用一些东西将该数组放在边界上,并使用 <stddef.h>
.
中的 offsetof()
宏
个人而言,我会使用 union
,基于旧习惯和偶尔使用 Visual C++ for C。但是 uint32_t 是 C99 类型,如果你也有 C11 支持你可以使用 _Alignas()
。这样,您的结构可能看起来像:
#define ALIGN_TYPE double /* if this is the worst-case type */
#define ALIGN_UNIT ((sizeof)(ALIGN_TYPE))
#define ALIGN_SIZE(n) (((size_t)(n) + ALIGN_UNIT - 1) & ~(ALIGN_UNIT-1))
typedef struct layout
{
size_t size; /* or use uint32_t if you prefer */
_Alignas(ALIGN_UNIT) char data[1];
} layout;
#define HEADER_SIZE (offsetof(layout, data))
这使得除了 worst-case 对齐类型之外的大多数内容都是符号化的。您将分配组合的 header 加数据数组:
layout *ptr = (layout*) malloc(HEADER_SIZE + ALIGN_SIZE(number_of_bytes));
ptr->size = HEADER_SIZE;
ALIGN_SIZE 类型实际上不是符号常量,除非 C99/C11 更改了 sizeof
的定义。例如,您不能使用它来计算普通数组维度。如果这是一个问题,您可以硬编码文字数字,例如 8 表示典型的双精度数字。请注意 long double
在许多 x86 实现上的大小(10 字节)有问题。如果您打算将分配单元基于类型,那么 long double
可能不是您的最佳选择。
我练习用C实现一个内存管理器
我想要具有各种长度和自我描述的结构。 所以,我偷看了一本POSIX教科书的东西,像这样:
struct layout
{
uint32_t size; // array size in bytes, include space after the struct
uchar_t data[1];
};
// But, is next line correct?
layout *val = malloc (array_memory_in_bytes + sizeof (uint32_t) - 1);
// Where does a static array keep the pointer for using it?
如果我在不间断的内存中一个接一个地拥有多个这样的结构,并且我希望能够遍历它们。我可以写点像这样的东西吗:
layout *val1 = pointer;
layout *val2 = val1 + val1.size + sizeof (val1.size);
或者你能推荐我一个更好的方法吗?
它的标准 C 版本称为 灵活数组成员,它看起来像:
struct layout
{
uint32_t size;
uchar_t data[];
};
// allocate one of these blocks (in a function)
struct layout *val = malloc( sizeof *val + number_of_bytes );
val->size = number_of_bytes;
代码 val1->data + val1->size
将为您提供 space 您刚刚 malloc
的最后一个指针。
但是,您不能迭代一个 malloc
' 块的末尾并希望击中另一个 malloc
' 个块。要实现这个想法,您必须 malloc
一个大块,然后在其中放置各种 struct layout
对象,注意 alignment.
在这种方法中,最好还存储每个 struct layout
所在位置的索引。从理论上讲,您可以每次都从头开始浏览列表,添加 size
然后进行对齐调整;但这会很慢,而且这意味着你无法处理中间的块被释放并重新 "allocated".
如果这是 malloc
的直接替代品,那么实际上有两个对齐注意事项:
- 对齐
struct layout
data
必须对齐任何可能的类型
解决这个问题的最简单方法是也为任何可能的类型对齐 struct layout
。这可能看起来像(注意:需要 #include <stdint.h>
):
struct layout
{
uint64_t size; // may as well use 64 bits since they're there
_Alignas(max_align_t) uchar_t data[];
};
另一种方法可能是将 size
保持在 32 位并加入 pragma pack
以防止填充;那么你需要使用一些额外的复杂性来确保 struct layout
被放置在 max_align_t
字节边界之前 4 个字节,等等。我建议先用简单的方法来获取代码 运行;然后稍后您可以返回并尝试此更改,以便根据需要节省几个字节的内存。
替代方法:
- 将
struct layout
的每个实例及其尾随数据保存在单独的分配中。 - 将
data
更改为指向malloc
的指针 space;那么您可以将所有struct layout
对象保存在一个数组中。
一般的想法可行,但只有当 most-severe 边界对齐情况是 int 时,该特定结构才可行。
内存管理器,尤其是可能是 back-end 实现 malloc()
的内存管理器,必须知道 worst-case 边界是什么。数据的实际开始必须在该边界上,以满足分配的内存适当对齐以存储任何数据类型的一般要求。
完成此操作的最简单方法是使 layout
结构描述的长度分配 header 和实际分配大小都是该对齐单元的倍数。
无论如何,您不能将数据的开头描述为结构成员,并且该结构的大小就是 header 的大小。 C 不支持 zero-length 字段。您应该使用一些东西将该数组放在边界上,并使用 <stddef.h>
.
offsetof()
宏
个人而言,我会使用 union
,基于旧习惯和偶尔使用 Visual C++ for C。但是 uint32_t 是 C99 类型,如果你也有 C11 支持你可以使用 _Alignas()
。这样,您的结构可能看起来像:
#define ALIGN_TYPE double /* if this is the worst-case type */
#define ALIGN_UNIT ((sizeof)(ALIGN_TYPE))
#define ALIGN_SIZE(n) (((size_t)(n) + ALIGN_UNIT - 1) & ~(ALIGN_UNIT-1))
typedef struct layout
{
size_t size; /* or use uint32_t if you prefer */
_Alignas(ALIGN_UNIT) char data[1];
} layout;
#define HEADER_SIZE (offsetof(layout, data))
这使得除了 worst-case 对齐类型之外的大多数内容都是符号化的。您将分配组合的 header 加数据数组:
layout *ptr = (layout*) malloc(HEADER_SIZE + ALIGN_SIZE(number_of_bytes));
ptr->size = HEADER_SIZE;
ALIGN_SIZE 类型实际上不是符号常量,除非 C99/C11 更改了 sizeof
的定义。例如,您不能使用它来计算普通数组维度。如果这是一个问题,您可以硬编码文字数字,例如 8 表示典型的双精度数字。请注意 long double
在许多 x86 实现上的大小(10 字节)有问题。如果您打算将分配单元基于类型,那么 long double
可能不是您的最佳选择。