You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x86_64平台malloc_chunk内存布局结构及实验疑问解析

(底部已更新部分解释内容)

我正在研究malloc的工作原理,在对chunk结构有基础了解后,决定编写一个小程序深入查看其实际运行逻辑。

#include <stdio.h>
#include <stdlib.h>
/*
An allocated chunk looks like this:
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk, if unallocated (P clear)  |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of chunk, in bytes                     |A|M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             User data starts here...                          .
            .                                                               .
            .             (malloc_usable_size() bytes)                      .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             (size of chunk, but used for application data)    |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of next chunk, in bytes                |A|0|1|
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
int main()
{
    void *a = malloc(24);
    void *b = malloc(16);
    void *chunk_b = NULL, *chunk_after_b = NULL, *chunk_a = NULL;
    
    chunk_a = a - 0x8; //8 byte before a (user data)
    chunk_b = a + malloc_usable_size(a);
    chunk_after_b = b + malloc_usable_size(b);

    *(int *)a = 0xa; 
    *(int *)b = 0xb;

    printf("a:0x%x malloc_usable_size:%d(0x%x)\n", a, malloc_usable_size(a), malloc_usable_size(a));
    printf("b:0x%x malloc_usable_size:%d(0x%x)\n\n", b, malloc_usable_size(b), malloc_usable_size(b));
    
    printf("chunk_a[0]:0x%x value:0x%04x \n", chunk_a, ((int *)chunk_a)[0]);
    printf("chunk_a[1]:0x%x value:0x%04x \n", &((int *)chunk_a)[1], ((int *)chunk_a)[1]);
    printf("chunk_a[2]:0x%x value:0x%04x \n\n", &((int *)chunk_a)[2], ((int *)chunk_a)[2]);
    
    printf("chunk_b[0]:0x%x value:0x%04x \n", chunk_b, ((int *)chunk_b)[0]);
    printf("chunk_b[1]:0x%x value:0x%04x \n", &((int *)chunk_b)[1], ((int *)chunk_b)[1]);
    printf("chunk_b[2]:0x%x value:0x%04x \n\n", &((int *)chunk_b)[2], ((int *)chunk_b)[2]);
    
    printf("chunk_after_b[0]:0x%x value:0x%04x \n", chunk_after_b, ((int *)chunk_after_b)[0]);
    printf("chunk_after_b[1]:0x%x value:0x%04x \n", &((int *)chunk_after_b)[1], ((int *)chunk_after_b)[1]);
    printf("chunk_after_b[2]:0x%x value:0x%04x \n\n", &((int *)chunk_after_b)[2], ((int *)chunk_after_b)[2]);

    free(a);
    free(b); 
    return 0;
}

运行结果如下:

a:0x625ca2a0 malloc_usable_size:24(0x18)
b:0x625ca2c0 malloc_usable_size:24(0x18)

chunk_a[0]:0x625ca298 value:0x0021
chunk_a[1]:0x625ca29c value:0x0000 
chunk_a[2]:0x625ca2a0 value:0x000a  -->mem (user data)
 
chunk_b[0]:0x625ca2b8 value:0x0021 
chunk_b[1]:0x625ca2bc value:0x0000 
chunk_b[2]:0x625ca2c0 value:0x000b -->mem (user data)
 
chunk_after_b[0]:0x625ca2d8 value:0x0411 
chunk_after_b[1]:0x625ca2dc value:0x0000 
chunk_after_b[2]:0x625ca2e0 value:0x6e756863

(gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) Target: x86_64-linux-gnu)

按照我的理解,malloc()会返回“User data”段的地址,因此将该地址减去8字节(对应“Size of chunk”和“Size of previous chunk”共8字节)就应该是chunk的起始地址(对应示例中的chunk_a);此外,将用户数据地址加上malloc_usable_size()的返回值就应该是下一个chunk的起始地址(对应示例中的chunk_b)。

但运行结果有部分内容不符合我的预期:我认为“Size of chunk”应该位于chunk_a[1]和chunk_b[1]的位置,但这两个位置的值都是0;而我认为是“Size of previous chunk”的chunk_a[0]和chunk_b[0]位置,值为0x21,换算为十进制是33,减去最低位P标志的1后得到32。

这个数值和实际chunk大小匹配:malloc_usable_size(a)返回的用户数据大小是24字节,加上8字节的chunk头部长度,正好是32字节的实际chunk大小。

想请教“Size of chunk”字段实际位于哪个位置?我之前的理解存在什么错误?


感谢评论区的所有建议,我补充做了一些测试后现在逻辑已经通顺了。

核心问题是我之前错误地将“Size of previous chunk”和“Size of chunk”都当成了4字节字段,而在x86_64编译环境下,两个字段都是8字节长度。

基于这个认知我重写了实验程序:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    
    int i=0;
    void *a = malloc(24);
    void *b = malloc(16);
    void *chunk_b = NULL, *chunk_a = NULL;
    
    chunk_a = (u_int64_t *)a - 2;
    chunk_b = a + malloc_usable_size(a);

    ((u_int8_t *)a)[0] = 0xa0;
    ((u_int8_t *)a)[23] = 0xa1;
    ((u_int8_t *)b)[0] = 0xb0;

    printf("a:0x%x malloc_usable_size:%d(0x%x)\n", a, malloc_usable_size(a), malloc_usable_size(a));
    printf("b:0x%x malloc_usable_size:%d(0x%x)\n\n", b, malloc_usable_size(b), malloc_usable_size(b));

    
    printf("chunk_a[0]:0x%x value:0x%016x \n", chunk_a, ((u_int64_t *)chunk_a)[0]);
    printf("chunk_a[1]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_a)[1], ((u_int64_t *)chunk_a)[1]);
    printf("chunk_a[2]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_a)[2], ((u_int64_t *)chunk_a)[2]);
    for(i = 0; i<24;i++)
    {
         printf("a[%d]:0x%x value:0x%02x \n", i, &((u_int8_t *)a)[i], ((u_int8_t *)a)[i]);
    }
    printf("\n");

    printf("chunk_b[0]:0x%x value:0x%016x \n", chunk_b, ((u_int64_t *)chunk_b)[0]);
    printf("chunk_b[1]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_b)[1], ((u_int64_t *)chunk_b)[1]);
    printf("chunk_b[2]:0x%x value:0x%016x \n\n", &((u_int64_t *)chunk_b)[2], ((u_int64_t *)chunk_b)[2]);

   free(b);
   free(a);
    
    return 0;
}

本次运行结果如下:

a:0xc2a4b2a0 malloc_usable_size:24(0x18)
b:0xc2a4b2c0 malloc_usable_size:24(0x18)

chunk_a[0]:0xc2a4b290 value:0x0000000000000000
chunk_a[1]:0xc2a4b298 value:0x0000000000000021
chunk_a[2]:0xc2a4b2a0 value:0x00000000000000a0
a[0]:0xc2a4b2a0 value:0xa0
a[1]:0xc2a4b2a1 value:0x00
a[2]:0xc2a4b2a2 value:0x00
a[3]:0xc2a4b2a3 value:0x00
a[4]:0xc2a4b2a4 value:0x00
a[5]:0xc2a4b2a5 value:0x00
a[6]:0xc2a4b2a6 value:0x00
a[7]:0xc2a4b2a7 value:0x00
a[8]:0xc2a4b2a8 value:0x00
a[9]:0xc2a4b2a9 value:0x00
a[10]:0xc2a4b2aa value:0x00
a[11]:0xc2a4b2ab value:0x00
a[12]:0xc2a4b2ac value:0x00
a[13]:0xc2a4b2ad value:0x00
a[14]:0xc2a4b2ae value:0x00
a[15]:0xc2a4b2af value:0x00
a[16]:0xc2a4b2b0 value:0x00
a[17]:0xc2a4b2b1 value:0x00
a[18]:0xc2a4b2b2 value:0x00
a[19]:0xc2a4b2b3 value:0x00
a[20]:0xc2a4b2b4 value:0x00
a[21]:0xc2a4b2b5 value:0x00
a[22]:0xc2a4b2b6 value:0x00
a[23]:0xc2a4b2b7 value:0xa1

chunk_b[0]:0xc2a4b2b8 value:0x0000000000000021
chunk_b[1]:0xc2a4b2c0 value:0x00000000000000b0
chunk_b[2]:0xc2a4b2c8 value:0x0000000000000000

我之前误以为两个字段都是4字节,是因为如果我从用户数据起始地址(本例中为0xa77ef2a0)加上malloc_usable_size()的返回值(本例中为0x18),得到的nextchunk位置和4字节字段的假设不匹配。

我之前的另一个误解是:如果当前chunk的“Size of chunk”字段最低位P标志被置位,代表前一个chunk处于已分配状态,此时当前chunk的“Size of previous chunk”字段会被算作前一个chunk的数据域的一部分。

本次实验中我将a指向的已分配内存的最后一个字节(地址0xc2a4b2b7)设为0xa1,紧跟其后的地址0xc2a4b2b8正好是chunk_b的“Size of chunk”字段,和上述逻辑完全吻合。

目前该逻辑已经完全符合我的认知,如果还有理解错误的地方欢迎指正,再次感谢各位的评论建议。


内容的提问来源于stack exchange,提问作者LLAN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 13:15:04