【问题标题】:Basic understanding of the heap堆的基本理解
【发布时间】:2015-02-19 05:35:01
【问题描述】:

我想了解更多关于堆上发生的事情。所以我看下面的C代码。它基本上只是在堆上为两个变量分配内存:

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

int main(int argc, char* argv[])
{
    char *char_ptr;
    int *int_ptr;
    int mem_size;

    if(argc < 2)
        mem_size = 50;
    else
        mem_size = atoi(argv[1]);

    printf("\t[+] allocating %d bytes of memory on the heap for char_ptr\n", mem_size);
    char_ptr = (char *) malloc(mem_size);
    if(char_ptr == NULL)
    {
        fprintf(stderr, "Error: could not allocate heap memory. \n");
        exit(-1);
    }

    strcpy(char_ptr, "This is memory located on the heap.");
    printf("char_ptr (%p) --> '%s'\n", char_ptr, char_ptr);

    printf("\t[+] allocating 12 bytes of memory on the heap for int_ptr\n");
    int_ptr = (int * ) malloc(12);
    if(int_ptr == NULL)
    {
        fprintf(stderr, "Error: could not allocate heap memory.");
        exit(-1);
    }

    *int_ptr = 31337;
    printf("int_ptr  (%p) --> %d\n", int_ptr, *int_ptr);

    printf("\t[-] freeing char_ptr's heap memory...\n");
    free(char_ptr);

    printf("\t[+] allocating another 15 bytes for char_ptr\n");
    char_ptr = (char *) malloc(15);
    if(char_ptr == NULL)
    {
        fprintf(stderr,"Error: could not allocate heap memory.\n");
        exit(-1);
    }

   strcpy(char_ptr, "new memory");
   printf("char_ptr (%p) --> '%s'\n", char_ptr, char_ptr);

   free(int_ptr);
   free(char_ptr);
}

此代码的输出如下所示:

    [+] allocating 50 bytes of memory on the heap for char_ptr
char_ptr (0x8827008) --> 'This is memory located on the heap.'
    [+] allocating 12 bytes of memory on the heap for int_ptr
int_ptr  (0x8827040) --> 31337
    [-] freeing char_ptr's heap memory...
    [+] allocating another 15 bytes for char_ptr
char_ptr (0x8827050) --> 'new memory'

所以我猜 char_ptr 是指向分配内存的开头(0x8827008),对吧?

由于分配了 50 个字节,因此该内存的末尾应指向地址 0x882702A。下一个内存分配从地址 0x8827040 开始。我的问题是:为什么 int_ptr 不指向 0x882702B (第一次内存分配后的下一个地址)?或者换句话说:0x772702A0x8827040 之间的内存发生了什么?

【问题讨论】:

  • 是什么让你认为它应该指向下一个地址? malloc可以随意分配物品;它不是堆栈或凹凸分配器。
  • 标准警告:请do not castmalloc()的返回值。
  • @SouravGhosh 感谢您提供的信息!

标签: c memory-management heap-memory


【解决方案1】:

谢谢大家的回答!他们帮助很大!我对堆的概念有一个根本性的误解。

总结一下已经说过的话:

  1. 除了实际值之外,还有每个内存块的“隐藏”信息
  2. 堆不需要将各个内存块“串在一起”

很遗憾,我不能给你们任何人投票,因为我没有 15 声望。我希望你原谅我;-)

【讨论】:

    【解决方案2】:

    可能是因为堆的控制信息是内联存储的。

    换句话说,每个块可能在其前面有一个“隐藏”部分,其中包含控制信息,如块大小、到下一个块的链接、检测损坏的标记等等。

    它还可能为您提供比请求更多的内存,例如确保它是 16 个字节的倍数(尽管您仍然只能使用您要求的内容)。

    然而,最重要的是,您不能依赖任何这些。它可能会因不同的实现、不同的版本或代码运行的日期而改变:-)

    您可以确定的是标准中规定的内容 - 它会为您提供 NULL 或可以在任何地方的可用地址。

    【讨论】:

      【解决方案3】:

      malloc() 不一定在下一个内存地址分配。以下是为什么不这样做的一些原因:

      • malloc() 只保证返回一些分配的内存。它的工作原理取决于malloc() - 从接口定义的角度来看,它是一个黑匣子。弄清楚里面发生了什么可能很有趣,但不是标准的一部分。

      • malloc() 会将分配对齐到方便的边界(通常是 64 位平台上的 64 位边界)。

      • 其他库调用之前可能有 free()'d 块,因此您会在一个空洞内获得分配 - 即在之前 free()d 块内存中。

      • 描述堆的内部数据结构紧接在分配数据结构之前。在一个简单的实现中,它们将是一个链表。

      • malloc() 从多个地方获取内存;是的,它使用堆(和brk/sbrk),但对于更大的分配,它将使用mmap()MAP_ANON

      所有这些因素都可能导致分配不在“下一个内存地址”。

      【讨论】:

        【解决方案4】:

        具体情况取决于您的 C 库(又取决于您的操作系统)。您看到的现象有两个因素:

        1. 从 malloc 获得的每个内存块都以标头为前缀(通常为 16 个字节)。但是,malloc 只返回一个指向标头之后的指针。
        2. 每个内存块必须对齐到最大对齐单元的倍数。通常(取决于微处理器),最大对齐为 8 个字节。用于实现对齐的字节称为填充。

        因此,以 ...2A 结尾的块被填充到 ...30,然后添加 16 字节的 malloc 标头,并且您的下一个块从 ...40 开始。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-09-04
          • 1970-01-01
          • 2013-07-19
          • 1970-01-01
          • 1970-01-01
          • 2021-05-02
          • 2010-12-29
          • 2019-08-06
          相关资源
          最近更新 更多