它似乎没有意义,除非我们只是忽略一个段开头的任何潜在的多余空间,然后让第一个分配的块位于8的第一个倍数(其对应的第一个头是该地址-4) .这将在未使用之前留下许多字节.这是普遍做的吗?
编辑: 感谢paxdiablo的详细说明如下.这对16字节标题都有意义.但是,我正在使用一个4字节的标题,看起来像这样:
struct mhdr {
int size; // size of this block
} tMallocHdr;
Run Code Online (Sandbox Code Playgroud)
现在,如果我的堆开始的地址是8的倍数,并且malloc返回的任何地址需要是8的倍数,并且我需要使用4个字节的标题,我似乎被迫"浪费"第一个我的堆的4个字节.例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
^
(heap starts)
Run Code Online (Sandbox Code Playgroud)
如果堆在地址8处的胡萝卜上面开始,使用本例中的寻址方案,我可以在malloc调用之后返回给用户的第一个可返回地址为16; 我需要4个字节的标头,第一个地址是8的倍数,它允许4个字节的标题是16(标题从12开始).这意味着我浪费了内部堆内存的前4个字节来排队(8-11).
这是可以接受的牺牲,还是我在考虑这个错误?
一般来说,是在所分配的区域的块标题浪费的空间.我看到的许多实现在返回的地址之前使用了一个16字节(我在这里使用经典的字节定义,八位之一,而不是ISO C定义)标头malloc
,并填充分配的区域最后也是16个字节.
这极大地简化了用于分配的算法,并且还保证返回的内存地址将针对体系结构进行适当的对齐.
但请记住,这是一个实现细节,而不是C标准的功能.如果没有对齐要求并且内存分配限制为255个字节,那么只浪费一个字节(尽管以更复杂的算法为代价)是非常合理的.
你可以拥有一个只能分配256字节块的嵌入式应用程序是非常合理的,在这种情况下你可以有一个简单的基于位图的分配器(位图存储在别处,而不是与分配的内存块一致),浪费每个块只有一位(我们之前在低内存环境中完成了这一点).
或者你可能在一个大的地址空间和内存环境中有一个分配器,无论你要求什么,它都能为你提供4G.然后没有标题的浪费,但可能很多填充:-)
或者你可能会从特定尺寸的竞技场中获得一块(竞技场A为1-64字节,竞技场B为65-128等).这意味着不需要标头,但仍然允许可变尺寸(最大化)和比上述4G解决方案少得多的浪费.
底线,这取决于实施.
在一个(相当简单的)实现中malloc
,你可以有一个双重链接的"块"列表,由分配器给出.这些块由头部和数据部分组成,为确保数据部分的对齐正确,头部必须在16字节边界上,并且是16字节长度的倍数(这是16字节对齐要求) - 您的实际要求可能不那么严格).
此外,块本身被填充,因此它是16字节的倍数,因此下一个块也适当地对齐.
这保证了任何块的数据部分,即给予调用者的地址malloc
,是正确对齐的.
所以你可能会在那个区域浪费掉.标头(具有4字节整数和指针)可能只是:
struct mhdr {
int size; // size of this block.
struct mhdr *prev; // prev chunk.
struct mhdr *next; // next chunk.
int junk; // for padding.
} tMallocHdr;
Run Code Online (Sandbox Code Playgroud)
意味着这16个字节中的4个将被浪费掉.有时,这是满足其他要求(对齐)所必需的,并且在任何情况下,您都可以将该填充空间用于其他目的.正如一位评论者指出的那样,保护字节可用于检测某些形式的竞技场腐败:
struct mhdr {
int guard_bytes; // set to 0xdeadbeef to detect some corruptions.
int size; // size of this block.
struct mhdr *prev; // prev chunk.
struct mhdr *next; // next chunk.
} tMallocHdr;
Run Code Online (Sandbox Code Playgroud)
而且,虽然填充在技术上是浪费空间,但只有在整个舞台上占相当大的比例时才变得重要.如果你要分配4K块内存,那么四个字节的浪费只是总大小的千分之一.实际上,作为用户的浪费可能是标题的整个16个字节,因为那是你不能使用的内存,所以它大约是0.39%(16 /(4096 + 16)).
这就是为什么malloc的链接字符列表是一个非常糟糕的想法 - 你倾向于使用,为每个字符:
这将使您的0.39%数字变为96.9%的浪费(31 /(31 + 1)).
并且,在回答您的进一步问题时:
这是可以接受的牺牲,还是我在考虑这个错误?
我会说,是的,这是可以接受的.通常,您分配较大的内存块,其中四个字节不会对宏的方案产生影响.正如我之前所说,如果你分配了很多小东西,这不是一个好的解决方案,但这不是一般的用例malloc
.