确定C中动态分配的内存的大小

s_i*_*bhu 56 c memory size allocation dynamic

有没有办法在C中找出动态分配内存的大小?

例如,之后

char* p = malloc (100);
Run Code Online (Sandbox Code Playgroud)

有没有办法找出与之相关的内存大小p

Ale*_*lds 45

comp.lang.c常见问题列表·问题7.27 -

问:那么我可以查询malloc包以找出分配的块有多大吗?

答:不幸的是,没有标准或便携的方式.(有些编译器提供非标准扩展.)如果你需要知道,你必须自己跟踪它.(另见问题7.28.)


ars*_*ars 44

没有标准的方法来查找此信息.但是,某些实现提供msize了执行此操作的功能.例如:

但请记住,malloc将分配所请求大小的最小值,因此您应检查实现的msize变量是否实际返回对象的大小或实际在堆上分配的内存.


Art*_*ius 11

C心态是为程序员提供工具来帮助他完成工作,而不是提供改变其工作性质的抽象.如果以牺牲性能限制为代价,C还会尝试避免使事情变得更容易/更安全.

您可能想要对内存区域执行的某些操作仅需要区域开头的位置.这样的事情包括使用以null结尾的字符串,操作区域的前n个字节(如果已知该区域至少这么大),等等.

基本上,跟踪区域的长度是额外的工作,如果C自动执行,它有时会不必要地执行它.

许多库函数(例如fread())需要指向区域开头的指针,以及该区域的大小.如果您需要区域大小,则必须跟踪它.

是的,malloc()实现通常会跟踪区域的大小,但是它们可以间接地执行此操作,或者将其四舍五入到某个值,或者根本不保留它.即使他们支持它,与自己跟踪它相比,以这种方式找到大小可能会很慢.

如果您需要一个知道每个区域有多大的数据结构,C可以为您做到这一点.只需使用一个结构来跟踪区域的大小以及指向该区域的指针.

  • 可以说,鉴于存在这种“间接确定它的方法”,而且我无法想象它怎么可能不存在,这是图书馆中有用的信息,图书馆应该提供访问权限。否则,就像图书馆(=图书馆开发人员)在嘲笑图书馆用户(“当然,我必须知道这一点,但我不会告诉你,你必须自己跟踪,因为我不会被打扰!”)我认为这是 C 库中的一个根本错误。 (3认同)
  • 虽然这个答案并不能完全回答这个问题,但我很欣赏对这种不存在的事物的理性解释。 (2认同)
  • 如果编译器没有任何记录它的malloced块有多大,那么它在匹配free时会做什么? (2认同)

Gre*_*ill 7

不,C运行时库不提供这样的功能.

某些库可能提供可以获取此信息的特定于平台或编译器的函数,但通常跟踪此信息的方法是另一个整数变量.


Quu*_*one 6

每个告诉你不可能的人在技术上都是正确的(最好的正确类型)。

出于工程原因,依靠 malloc 子系统准确地告诉您已分配块的大小是一个坏主意。为了让自己相信这一点,假设您正在编写一个包含多个不同内存分配器的大型应用程序——也许您malloc在一部分中使用原始 libc ,operator new在另一部分中使用C++ ,然后在另一部分中使用某些特定的 Windows API。所以你有各种各样的void*飞来飞去。编写一个可以处理任何这些问题void*的函数是不可能的,除非您能以某种方式从指针的值中知道它来自哪个堆。

因此,您可能希望使用一些约定来包装程序中的每个指针,以指示指针来自何处(以及需要返回到何处)。例如,在 C++ 中,我们称之为std::unique_ptr<void>(对于需要operator delete'dstd::unique_ptr<void, D>的指针)或(对于需要通过其他机制返回的指针D)。如果你愿意,你可以在 C 中做同样的事情。无论如何,一旦您将指针封装在更大的更安全的对象中,这只是一小步struct SizedPtr { void *ptr; size_t size; },然后您就再也不需要担心分配的大小了。

然而。

您可能有正当理由想知道分配的实际基础大小,这也是有充分理由的。例如,也许您正在为您的应用程序编写一个分析工具,它将报告每个子系统使用的实际内存量,而不仅仅是程序员认为他正在使用的内存量。如果您的每个 10 字节分配在幕后秘密使用 16 字节,那么知道这一点很高兴!(当然也会有其他开销,您不会以这种方式衡量。但是还有其他工具可以完成这项工作。)或者您可能只是在调查realloc平台上的行为。或者,您可能想“汇总”不断增长的分配的容量以避免过早未来重新分配。例子:

SizedPtr round_up(void *p) {
    size_t sz = portable_ish_malloced_size(p);
    void *q = realloc(p, sz);  // for sanitizer-cleanliness
    assert(q != NULL && portable_ish_malloced_size(q) == sz);
    return (SizedPtr){q, sz};
}
bool reserve(VectorOfChar *v, size_t newcap) {
    if (v->sizedptr.size >= newcap) return true;
    char *newdata = realloc(v->sizedptr.ptr, newcap);
    if (newdata == NULL) return false;
    v->sizedptr = round_up(newdata);
    return true;
}
Run Code Online (Sandbox Code Playgroud)

要获取直接从 libc malloc 返回的非空指针后面的分配大小- 不是从自定义堆,也不是指向对象的中间 - 您可以使用以下特定于操作系统的 API,我为方便起见,已捆绑为“便携式”包装器功能。如果您发现此代码不起作用的常见系统,请发表评论,我会尽力修复它!

#if defined(__linux__)
// https://linux.die.net/man/3/malloc_usable_size
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_usable_size((void*)p);
}
#elif defined(__APPLE__)
// https://www.unix.com/man-page/osx/3/malloc_size/
#include <malloc/malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_size(p);
}
#elif defined(_WIN32)
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return _msize((void *)p);
}
#else
#error "oops, I don't know this system"
#endif

#include <stdio.h>
#include <stdlib.h>  // for malloc itself

int main() {
    void *p = malloc(42);
    size_t true_length = portable_ish_malloced_size(p);
    printf("%zu\n", true_length);
}
Run Code Online (Sandbox Code Playgroud)

测试:


Les*_*win 5

这是我看到的创建标记指针以存储大小和地址的最佳方法。所有指针函数仍将按预期工作:

被盗:https : //stackoverflow.com/a/35326444/638848

您还可以为malloc实现包装器,并在malloc返回的指针之前自由添加标签(如分配的大小和其他元信息)。实际上,这是c ++编译器使用对虚拟类的引用标记对象的方法。这是一个工作示例:

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

void * my_malloc(size_t s) 
{
  size_t * ret = malloc(sizeof(size_t) + s);
  *ret = s;
  return &ret[1];
}

void my_free(void * ptr) 
{
  free( (size_t*)ptr - 1);
}

size_t allocated_size(void * ptr) 
{
  return ((size_t*)ptr)[-1];
}

int main(int argc, const char ** argv) {
  int * array = my_malloc(sizeof(int) * 3);
  printf("%u\n", allocated_size(array));
  my_free(array);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

与具有大小和指针的结构相比,此方法的优势

 struct pointer
 {
   size_t size;
   void *p;
 };
Run Code Online (Sandbox Code Playgroud)

就是您只需要替换malloc和free调用即可。所有其他指针操作都不需要重构。

  • 您还应该重新定义重新分配 (2认同)
  • 这个答案假设“&amp;ret[1]”是[“适当对齐的,以便它可以分配给具有基本对齐要求的任何类型对象的指针,然后用于访问此类对象或此类对象的数组分配的空间”](https://port70.net/~nsz/c/c11/n1570.html#7.22.3p1)。可能不是。 (2认同)