【问题标题】:How to get memory block length after malloc?malloc后如何获取内存块长度?
【发布时间】:2011-03-27 17:54:58
【问题描述】:

我认为我无法像 Java 中的简单 .length 函数那样检索已分配内存块的长度。但是,我现在知道当malloc() 分配块时,它会分配额外的字节来保存包含块大小的整数。该整数位于块的开头;实际返回给调用者的地址指向刚刚超过这个长度值的位置。问题是,我无法访问该地址来检索块长度。

#include <stdlib.h>
#include <stdio.h>
int main(void)
{
        char *str;
        str = (char*) malloc(sizeof(char)*1000);
        int *length;
        length = str-4; /*because on 32 bit system, an int is 4 bytes long*/
        printf("Length of str:%d\n", *length);
        free(str);
}

**编辑: 我终于做到了。问题是,它一直给出 0 作为长度而不是我系统上的大小,因为我的 Ubuntu 是 64 位的。我将 str-4 更改为 str-8,现在可以使用了。

如果我将大小更改为 2000,它会生成 2017 作为长度。但是,当我更改为 3000 时,它会给出 3009。我使用的是 GCC。

【问题讨论】:

  • 您所描述的完全取决于特定的 malloc 实现以您描述的方式存储信息。 malloc 的实现不一定必须跟踪分配了多少内存(不太可能,但有可能,因为 API 不要求将此信息公开给调用者。)
  • @Joe:例如,您可以相当容易地实现基于池的分配器,例如,所有大小为 1-8 的分配来自一个池,9-16 来自另一个池,17-32从另一个,等等。然后,每个分配可能会存储一个指向其池的指针,而不是存储在可用内存之前的大小。不过,大分配必须区别对待。

标签: c malloc


【解决方案1】:

您不必自己跟踪!

size_t malloc_usable_size (void *ptr);

但它返回分配的内存块的实际大小! 不是您传递给 malloc 的大小!

【讨论】:

  • 需要注意的是,这个函数不是 C 标准的一部分,是一个 gnulib 扩展
  • @Hydroper 绝对不是。检测 GCC,而不是 GNU C 库。请改用__GNU_LIBRARY__
【解决方案2】:

你所做的绝对是错误的。虽然几乎可以肯定分配块之前的字与大小有关,但它可能在未使用的位中包含一些额外的标志或信息。根据实现的不同,这些数据甚至可能在 high 位中,这会导致您读取完全错误的长度。也有可能小分配(例如 1 到 32 个字节)被打包到没有标题的特殊小块页面中,在这种情况下,分配块之前的单词只是另一个块的一部分,并且与大小无关你正在检查的块。

停止这种被误导和危险的追求。如果你需要知道malloc获得的块的大小,那你就做错了。

【讨论】:

  • 我明白了。谢谢。这就是为什么它对每种尺寸都有不同的价值。我只是尝试看看它是如何工作的,我不会在实际中这样做。但是,书中有一个练习需要根据其解释的实现来实现 malloc() 和 free(),所以我只想试一试。
  • 酷,+1 作为实验的一部分来“弄清楚它是如何工作并尝试实施”而不是为了一些可怕的黑客。
  • 你知道过滤掉它附加到块的所有附加标志的方法,所以我得到了纯内存大小吗?但是,通过猜测,我认为我无法改变这一点,并且必须改变我自己的方式。另外,我不知道为什么 sizeof(int) 给出 4,而我的系统是 64 位?有没有办法让 sizeof 知道我的 64 位系统?
  • sizeof(int) 在绝大多数 unix 系统上始终为 4,并且可能永远都是。如果您需要系统字长,请使用long 或更好的size_t
  • @texasbruce:除非您正在为您将要使用的特定 C 实现(库和编译器)的特定版本进行编程,否则它本质上是危险和错误的,仅此而已。即便如此,做出这种假设是危险和错误的,因为稍后可能会在您正在使用的实现中发现关键错误,并且您需要升级,并且如果您基于旧版本所做的假设是新版本不再适用,您的程序将灾难性地中断。
【解决方案3】:

我建议您通过编译和链接定义 my_malloc() 的文件然后覆盖默认值来创建自己的 malloc 包装器,如下所示:

// my_malloc.c

#define malloc(sz) my_malloc(sz)

typedef struct {
    size_t size;
} Metadata;

void *my_malloc(size_t sz) {
    size_t size_with_header = sz + sizeof(Metadata);
    void* pointer = malloc(size_with_header);

    // cast the header into a Metadata struct
    Metadata* header = (Metadata*)pointer;
    header->size = sz;    
    // return the address starting after the header 
    // since this is what the user needs
    return pointer + sizeof(Metadata);
}

那么您总是可以通过减去 sizeof(Metadata) 来检索分配的大小,将该指针转换为 Metadata 并执行 metadata->size:

Metadata* header = (Metadata*)(ptr - sizeof(Metadata));
printf("Size allocated is:%lu", header->size); // don't quote me on the %lu ;-)

【讨论】:

  • 这是一个比查看 gcc 源代码更好的解决方法。
  • 值得注意的是,包装器返回的内存在大多数架构上将不再对齐。我应该知道,我写了同样的代码,我的代码依赖于对齐方式在我面前炸毁了。
  • 这是有道理的,这段代码只能在严格控制条件下用于调试目的。
  • 不允许使用带有 void* 的 FYI 指针运算,因此此代码将无法在兼容的编译(例如,带有 -pedantic 的 GCC)上编译。
  • @Thomas 要修复对齐,您可以使用带有 max_align_t 类型成员的联合: typedef union { size_t size; max_align_t ma; } 元数据;
【解决方案4】:

你不应该那样做。如果您想知道您分配了多少内存,您需要自己跟踪它。

查看返回给您的内存块(在 malloc 返回的指针之前,或在该指针 + 您请求的字节数之后)将导致未定义的行为。对于给定的 malloc 实现,它可能在实践中有效,但依赖它并不是一个好主意。

【讨论】:

    【解决方案5】:

    这不是标准 C。但是,它应该适用于 Windows 操作系统,并且可能也适用于其他操作系统,例如 Linux (msize?) 或 Mac (alloc_size?)。

    size_t _msize( void *memblock );

    _msize() 返回堆中分配的内存块的大小。

    查看此链接: http://msdn.microsoft.com/en-us/library/z2s077bc.aspx

    【讨论】:

      【解决方案6】:

      这取决于实现

      【讨论】:

      • @Amumu 如何做到这一点取决于编译器和架构。
      • 它更多地取决于 malloc() 本身的实现,而不是真正的编译器。
      【解决方案7】:

      您分配的每个块前面都有一个块描述符。问题是,它取决于系统架构。 尝试为您自己的系统找到块描述符大小。试着看看你的系统 malloc 手册页。

      【讨论】:

      • 不一定。看我的回答。
      猜你喜欢
      • 2014-07-17
      • 2020-09-27
      • 2021-01-10
      • 2010-11-15
      • 2021-04-25
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      相关资源
      最近更新 更多