C 内存管理器的不同长度结构?

C 内存管理器的不同长度结构?,c,memory-management,struct,C,Memory Management,Struct,我练习用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 + siz

我练习用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);
如果在不间断的内存中有几个这样的结构,我希望能够遍历它们。我能写点什么吗

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版本称为flexible array member,它看起来像:

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
将为您获取一个指针,该指针将越过刚刚
malloc
'd的空间末端

但是,您不能迭代一个
malloc
'd块的末尾,而希望命中另一个
malloc
'd块。要实现这个想法,您必须
malloc
一个大的块,然后在其中放置各种
struct layout
对象,要小心

在这种方法中,最好还存储每个
结构布局所在位置的索引。理论上,你可以每次从一开始就查看列表,添加
大小
,然后进行对齐调整;但这将是缓慢的,这也意味着你无法应付中间的一个块被释放和重新“分配”。 如果这是对
malloc
的替代,那么实际上有两个对齐注意事项:

  • 结构布局的对齐
  • 数据
    必须与任何可能的类型对齐
处理此问题的最简单方法是对齐任何可能类型的
struct layout
。这可能看起来像(注意:
#包括
必填项):

另一种方法是将
大小保持在32位,并加入
杂注包以防止填充;然后,您需要使用一些额外的复杂度来确保
结构布局
max\u align\t
字节边界之前放置4个字节,以此类推。我建议先用简单的方法完成,然后让代码运行;然后,如果需要,您可以返回并尝试此更改以节省一些字节的内存


备选办法:

  • 结构布局的每个实例及其尾部数据保留在单独的分配中
    
  • 数据
    更改为指向
    malloc
    'd空间的指针;然后可以将所有
    结构布局
    对象保留在一个数组中

总体思路可行,但只有在最严重的边界对齐情况是int的情况下,特定结构才会起作用

内存管理器,特别是可能是
malloc()
实现的后端的内存管理器,必须知道最坏情况的边界是什么。数据的实际开始必须在该边界上,以满足分配的内存对任何数据类型的存储进行适当对齐的一般要求

最简单的方法是使
布局
结构描述的长度分配头和实际分配大小都是该对齐单元的倍数

不管怎样,您都不能将数据的开头描述为一个结构成员,而将该结构的大小设置为头的大小。C不支持零长度字段。您应该使用一些东西将该数组置于边界上,并使用
中的
offsetof()

P>个人,我将使用 CuthOng/Cuth>,基于C语言的旧习惯和偶尔使用Visual C++,但是UTIN 32是一个C99类型,如果你还有C11支持,你可以使用。这样,您的结构可以看起来像:

#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))
这使得除最坏情况下的对齐类型外的所有内容都具有象征性。您将使用以下各项分配组合的标头加数据数组:

layout *ptr = (layout*) malloc(HEADER_SIZE + ALIGN_SIZE(number_of_bytes));
ptr->size = HEADER_SIZE;

但是,除非C99/C11更改了
sizeof
的定义,否则ALIGN\u SIZE类型实际上不是一个符号常量。例如,不能使用计算普通数组维度。如果有问题的话,你可以硬编码一个文字数字,比如8代表一个典型的双精度数字。请注意,在许多x86实现中,
longdouble
的大小有问题(10字节)。如果要根据类型来确定分配单位,则
long double
可能不是最佳选择。

似乎相当合理,但您可能希望/需要在对象之间添加对齐。如果您有C99编译器,请使用灵活的数组成员。根据平台的不同,您可能需要考虑“大小”和“数据”之间的填充。如果可以,您应该明确定义结构的打包,以确保没有任何额外空间。@doynax,感谢您使用“灵活数组成员”一词,我找到了我问题的答案@joshpoley,行“struct layout val=malloc(sizeof*val+(字节数-1));/non-flexible*/”不依赖于打包方式关于“静态数组在哪里保存指针以供使用?”,它不依赖于打包方式。这样的指针是根据需要创建的右值。如果这样做,必须考虑对齐:每个结构应在4字节边界上对齐,以便正确对齐
uint32\t size
成员。要么将大小保持为4的倍数,要么在结构之间添加一些额外的填充。@因此实际问题是
数据中的字节数可能不是对齐的倍数;因此,在放置另一个
struct layout
@之前,您必须插入一些填充字节。因此,olitary:使用
sizeof(struct layout)
实际上无助于对齐,因为您添加数组长度时没有对齐它。这实际上让事情变得更糟,因为在C99之前的版本中,
struct header
在1字节数组之后包含3字节的填充。您应该使用偏移量(结构布局、数据)+align4(字节数)
来定义此定义