由 malloc 在 C 中分配的大小的意外输出 [关闭]

Posted

技术标签:

【中文标题】由 malloc 在 C 中分配的大小的意外输出 [关闭]【英文标题】:unexpected output of size allocated by malloc in C [closed] 【发布时间】:2012-08-12 04:01:59 【问题描述】:

我读到 malloc 实际上分配了 (required_size + 1) 个内存块,它将大小存储在第一个块中,并返回指向第二个块的指针。这样 free() 就知道要释放多少内存。所以,我写了一个小代码来输出这个大小。

int *p = (int *)malloc(100*sizeof(int));
printf("size = %d\n",p[-1]);

由于我为 100 个整数分配空间,我希望大小为 400。但输出为 409。50 个整数的输出为 209,1000 个整数的输出为 4009。有人可以解释为什么输出关闭9 个字节?

【问题讨论】:

这是未定义的行为和实现相关的。 如果您告诉我们您正在使用的编译器版本、您的架构以及您对 malloc 的特定实现,我们可能会给您一些答案。 这是一个有趣的假设,我希望您只是出于好奇而不是试图在任何地方使用它。在我的 amd64 系统上,用size_t 替换int 后,我得到817(与800 要求相比);但计数越小,数字或多或少都会减少。 是的..只是出于好奇才问的。实际上我已经使用这个网站 ideone.com 来运行这个代码。我没有检查过其他编译器。 【参考方案1】:

如果该值是分配大小,除了它依赖于实现之外,我会冒险猜测并说您有几个可能获得额外的9

对于未释放的分配,该数字可能总是奇数。鉴于大多数操作系统具有以高于 1 字节的粒度返回的内置内存分配函数,因此 malloc 的实现可能使用分配大小的第一位来跟踪分配是否已被释放。

malloc 实现也利用操作系统返回的分配的自然对齐,即将实际分配大小四舍五入到操作系统保证的对齐(这将占额外的 8 个字节) , 它还在分配结束时分配 4 个字节用作保护值。

但是,您可以从您正在使用的平台和编译器的文档中了解有关分配信息存储位置的更多信息。

【讨论】:

【参考方案2】:

假设实现是glibc(或类似的),下面可以在malloc.c的cmets中找到:

Minimum overhead per allocated chunk:   4 or 8 bytes
   Each malloced chunk has a hidden word of overhead holding size
   and status information.

Minimum allocated size: 4-byte ptrs:  16 bytes    (including 4 overhead)
          8-byte ptrs:  24/32 bytes (including, 4/8 overhead)

   When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte
   ptrs but 4 byte size) or 24 (for 8/8) additional bytes are
   needed; 4 (8) for a trailing size field and 8 (16) bytes for
   free list pointers. Thus, the minimum allocatable size is
   16/24/32 bytes.

这就解释了开销的存在。

现在,对于'off by 1',标志负责。由于malloc() 分配的大小(实际上)总是 8 的倍数,因此三个最低有效位用于存储标志:

/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1

/* extract inuse bit of previous chunk */
#define prev_inuse(p)       ((p)->size & PREV_INUSE)


/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2

/* check for mmap()'ed chunk */
#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED)


/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
   from a non-main arena.  This is only set immediately before handing
   the chunk to the user, if necessary.  */
#define NON_MAIN_ARENA 0x4

/* check for chunk from non-main arena */
#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)

编辑:啊,我差点忘了。大小存储为size_t,而不是int,因此您应该使用该类型来访问它。

【讨论】:

以上是关于由 malloc 在 C 中分配的大小的意外输出 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中释放在 C++ 中分配的内存

为啥编译器在堆栈中分配的比需要的多?

C# Interop - 释放在非托管代码中分配的内存

在 xib 中分配的图像是不是缓存?

访问在函数中分配的变量[重复]

Firebase:保留在observeSingleEvent中分配的值[重复]