代码之家  ›  专栏  ›  技术社区  ›  Gaurav Ojha

了解堆中的内存分配

  •  -3
  • Gaurav Ojha  · 技术社区  · 6 年前

    我试图理解如何使用malloc在堆上分配内存,遇到了以下观察结果,但无法理解其背后的原因。如果有人能解释就好了。

    #include<stdio.h>
    #include<stdlib.h>
    
    void print_int_heap(unsigned int *ptr, int len)
    {
        printf("PREV_SIZE: [%08x]  SIZE: [%08x]  MEM: [%08x] for INT malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
    }
    void print_char_heap(char *ptr, int len)
    {
        printf("PREV_SIZE: [%08x]  SIZE: [%08x]  MEM: [%08x] for CHAR malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
    }
    
    int main() {
    
        unsigned int *ptr1 = malloc(20);
        print_int_heap(ptr1, 20);
        char *ptr2 = malloc(20)
        print_char_heap(ptr2, 20);
        return 0;
    }
    

    以上程序的输出是:

    PREV_SIZE: [0x00000000] SIZE: [0x00000019] MEM: [0x0804b008] for INT malloc(20)
    PREV_SIZE: [0x00000000] SIZE: [0x00000000] MEM: [0x0804b020] for INT malloc(20)
    

    我可以理解int malloc的输出,但我不明白char malloc的chunk size值为什么是0?

    3 回复  |  直到 6 年前
        1
  •  2
  •   rici    6 年前

    如果 ptr 是一个 int* , *(ptr - 1) sizeof(int) 参考文献。这通常是一个32位的量,从4个字节开始 ptr公司 .

    char* , *(ptr-1) sizeof(char) 就在什么之前 大小(字符) 始终为1;通常为值之前的单个字节中的8位量 ptr公司 .

    顺便说一下,你可以写作 ptr[-1] . 但正如上面的分析所显示的,这并不是你想要的。你想投吗 指向您认为在前面的对象的数据类型的指针 ptr公司 ,可能吧 uint32_t .

    从技术上讲,这都是未定义的行为,但如果 malloc 实现是在分配之前存储数据,并且您知道该数据的类型,我认为可以读取它。(尽管盯着系统函数的内部数据看总是有点粗鲁。)

    请注意,并非所有 实现也做同样的事情。你很可能会在别的地方找到一个长度相同的,或者根本找不到。

        2
  •  1
  •   Barmar    6 年前

    char *ptr ptr-1 从中的地址减去1字节 ptr . 但与 unsigned int *ptr ptr-1型 减法 sizeof(int) 从中的地址 ptr公司 .

    另外,当取消引用指针时,它只访问指针数据类型中的字节数。所以在 print_int_heap() , *(ptr-1) 返回一个 unsigned int ,在 print_char_heap() 它返回一个 char

    你应该写一个 print_heap() 函数,并将参数强制转换为调用者中的相应类型。

        3
  •  1
  •   Achal    6 年前

    预订人 丹尼斯·M·里奇

    而不是从编译的 固定大小 阵列, 马洛克 空闲块 . 每个块包含一个 大小 指向下一个块的指针 ,和 空间本身 malloc()返回的块如下所示

        points to          
        next free
         block       
          |                
       ---------------------------------------
       |       |   size   |                  |
       ---------------------------------------
       |       |          |..address returned to the user
       (ptr-2) (ptr-1)    ptr -->     
                          LSB              MSB
    

    在这里

    void print_int_heap(unsigned int *ptr, int len) {
        printf("PREV_SIZE: [%08x]  SIZE: [%08x]  MEM: [%08x] for INT malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
    }
    

    *(ptr-2) 打印内部值 "next free block" *(ptr-1) 打印内部值 "size" 块,即分配了多少内存,& ptr 打印用户返回的地址。注意这里 类型为 unsigned int* 所以 *(ptr-2) 意味着从 2*sizeof(int) 字节就在哪里之前

    void print_char_heap(char *ptr, int len){
        printf("PREV_SIZE: [%08x]  SIZE: [%08x]  MEM: [%08x] for CHAR malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
    }
    

    访问时 *(ptr-1)

        next free
         block        (ptr-1)--> *(ptr-1) prints data from ? marked location.
          |            |  
       ---------------------------------------
       |       | size |? |                  |
       ---------------------------------------
       |       |         |..address returned to the user
                         ptr -->     
                         LSB              MSB
    

    char* 意思是当你这么做的时候 它将从 sizeof(char) 字节就在哪里之前 点数。

    也更好用 valgrind 动态分配内存时,只需运行

    valgrind --leak-check=full -v ./your_exe
    

    valgrind . 例如,它可能表现出

    ==3193== Invalid read of size 4
    ==3193==    at 0x8048459: print_int_heap
    ==3193== Invalid read of size 4
    ==3193==    at 0x8048461: print_int_heap