简体   繁体   English

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

[英]Determine size of dynamically allocated memory in C

Is there a way in C to find out the size of dynamically allocated memory?在 C 中有没有办法找出动态分配的内存的大小?

For example, after例如,经过

char* p = malloc (100);

Is there a way to find out the size of memory associated with p ?有没有办法找出与p关联的内存大小?

There is no standard way to find this information.没有找到这些信息的标准方法。 However, some implementations provide functions like msize to do this.然而,一些实现提供了像msize这样的函数来做到这一点。 For example:例如:

Keep in mind though, that malloc will allocate a minimum of the size requested, so you should check if msize variant for your implementation actually returns the size of the object or the memory actually allocated on the heap.但是请记住,malloc 将分配请求的最小大小,因此您应该检查实现的 msize 变量是否实际返回对象的大小或实际分配在堆上的内存。

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

Q. So can I query the malloc package to find out how big an allocated block is?问:那么我可以查询malloc包以找出分配的块有多大吗?

A. Unfortunately, there is no standard or portable way. A. 不幸的是,没有标准或可移植的方式。 (Some compilers provide nonstandard extensions.) If you need to know, you'll have to keep track of it yourself. (一些编译器提供非标准的扩展。)如果你需要知道,你必须自己跟踪它。 (See also question 7.28 .) (另见问题7.28 。)

The C mentality is to provide the programmer with tools to help him with his job, not to provide abstractions which change the nature of his job. C 思维是为程序员提供工具来帮助他完成工作,而不是提供改变其工作性质的抽象。 C also tries to avoid making things easier/safer if this happens at the expense of the performance limit.如果以性能限制为代价发生这种情况,C 还会尝试避免使事情变得更容易/更安全。

Certain things you might like to do with a region of memory only require the location of the start of the region.您可能想对内存区域执行的某些操作只需要该区域的起始位置。 Such things include working with null-terminated strings, manipulating the first n bytes of the region (if the region is known to be at least this large), and so forth.这些事情包括处理以空字符结尾的字符串,操作该区域的前n个字节(如果已知该区域至少有这么大),等等。

Basically, keeping track of the length of a region is extra work, and if C did it automatically, it would sometimes be doing it unnecessarily.基本上,跟踪区域的长度是额外的工作,如果 C 自动完成,有时会不必要地这样做。

Many library functions (for instance fread() ) require a pointer to the start of a region, and also the size of this region.许多库函数(例如fread() )需要一个指向区域开头的指针,以及该区域的大小。 If you need the size of a region, you must keep track of it.如果您需要区域的大小,则必须对其进行跟踪。

Yes, malloc() implementations usually keep track of a region's size, but they may do this indirectly, or round it up to some value, or not keep it at all.是的, malloc() 实现通常会跟踪区域的大小,但它们可能会间接执行此操作,或者将其四舍五入到某个值,或者根本不保留它。 Even if they support it, finding the size this way might be slow compared with keeping track of it yourself.即使他们支持它,与自己跟踪它相比,以这种方式找到大小可能会很慢。

If you need a data structure that knows how big each region is, C can do that for you.如果您需要一个知道每个区域有多大的数据结构,C 可以为您做到。 Just use a struct that keeps track of how large the region is as well as a pointer to the region.只需使用一个结构来跟踪区域有多大以及指向该区域的指针。

No, the C runtime library does not provide such a function.不,C 运行时库不提供这样的功能。

Some libraries may provide platform- or compiler-specific functions that can get this information, but generally the way to keep track of this information is in another integer variable.一些库可能提供特定于平台或编译器的函数来获取此信息,但通常跟踪此信息的方法是在另一个整数变量中。

Here's the best way I've seen to create a tagged pointer to store the size with the address.这是我见过的创建标记指针以存储地址大小的最佳方法。 All pointer functions would still work as expected:所有指针函数仍会按预期工作:

Stolen from: https://stackoverflow.com/a/35326444/638848盗取自: https : //stackoverflow.com/a/35326444/638848

You could also implement a wrapper for malloc and free to add tags (like allocated size and other meta information) before the pointer returned by malloc.您还可以为 malloc 实现一个包装器,并在 malloc 返回的指针之前自由添加标签(如分配的大小和其他元信息)。 This is in fact the method that a c++ compiler tags objects with references to virtual classes.这实际上是 C++ 编译器使用对虚拟类的引用来标记对象的方法。 Here is one working example:这是一个工作示例:

 #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; }

The advantage of this method over a structure with size and pointer这种方法相对于具有大小和指针的结构的优势

 struct pointer { size_t size; void *p; };

is that you only need to replace the malloc and free calls.就是你只需要替换 malloc 和 free 调用。 All other pointer operations require no refactoring.所有其他指针操作都不需要重构。

Everyone telling you it's impossible is technically correct (the best kind of correct).每个告诉你不可能的人在技术上都是正确的(最好的正确)。

For engineering reasons, it is a bad idea to rely on the malloc subsystem to tell you the size of an allocated block accurately.出于工程原因,依靠 malloc 子系统准确地告诉您已分配块的大小是一个坏主意。 To convince yourself of this, imagine that you were writing a large application, with several different memory allocators — maybe you use raw libc malloc in one part, but C++ operator new in another part, and then some specific Windows API in yet another part.为了让自己相信这一点,假设您正在编写一个包含多个不同内存分配器的大型应用程序——也许您在一部分中使用原始 libc malloc ,但在另一部分中使用 C++ operator new ,然后在另一部分中使用某些特定的 Windows API。 So you've got all kinds of void* flying around.所以你有各种各样的void*飞来飞去。 Writing a function that can work on any of these void* s impossible, unless you can somehow tell from the pointer's value which of your heaps it came from.编写一个可以处理这些void*任何一个的函数是不可能的,除非您能以某种方式从指针的值中知道它来自哪个堆。

So you might want to wrap up each pointer in your program with some convention that indicates where the pointer came from (and where it needs to be returned to).因此,您可能希望使用某种约定来包装程序中的每个指针,以指示指针来自何处(以及需要返回到何处)。 For example, in C++ we call that std::unique_ptr<void> (for pointers that need to be operator delete 'd) or std::unique_ptr<void, D> (for pointers that need to be returned via some other mechanism D ).例如,在 C++ 中,我们称之为std::unique_ptr<void> (对于需要被operator delete 'd 的指针)或std::unique_ptr<void, D> (对于需要通过某种其他机制D返回的指针) )。 You could do the same kind of thing in C if you wanted to.如果你愿意,你可以在 C 中做同样的事情。 And once you're wrapping up pointers in bigger safer objects anyway , it's just a small step to struct SizedPtr { void *ptr; size_t size; }无论如何,一旦你将指针封装在更大的更安全的对象中,这只是struct SizedPtr { void *ptr; size_t size; } struct SizedPtr { void *ptr; size_t size; } struct SizedPtr { void *ptr; size_t size; } and then you never need to worry about the size of an allocation again. struct SizedPtr { void *ptr; size_t size; }然后你永远需要约一个分配的大小担心一次。

However.然而。

There are also good reasons why you might legitimately want to know the actual underlying size of an allocation.您可能有正当理由想知道分配的实际基础大小,这也是有充分理由的。 For example, maybe you're writing a profiling tool for your app that will report the actual amount of memory used by each subsystem, not just the amount of memory that the programmer thought he was using.例如,也许您正在为您的应用程序编写一个分析工具,它将报告每个子系统使用的实际内存量,而不仅仅是程序员认为他正在使用的内存量。 If each of your 10-byte allocations is secretly using 16 bytes under the hood, that's good to know!如果您的每个 10 字节分配在幕后秘密使用了 16 字节,那就太好了! (Of course there will be other overhead as well, which you're not measuring this way. But there are yet other tools for that job.) Or maybe you're just investigating the behavior of realloc on your platform. (当然也会有其他开销,您不会以这种方式衡量。但是还有其他工具可以用于工作。)或者您可能只是在调查您平台上的realloc行为。 Or maybe you'd like to "round up" the capacity of a growing allocation to avoid premature reallocations in the future.或者,您可能希望“汇总”不断增长的分配容量,以避免将来过早地重新分配。 Example:例子:

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;
}

To get the size of the allocation behind a non-null pointer which has been returned directly from libc malloc — not from a custom heap, and not pointing into the middle of an object — you can use the following OS-specific APIs, which I have bundled up into a "portable-ish" wrapper function for convenience.要获取直接从 libc malloc 返回的非空指针后面的分配大小(不是从自定义堆,也不是指向对象的中间),您可以使用以下特定于操作系统的 API,我为方便起见,已捆绑为“便携式”包装器功能。 If you find a common system where this code doesn't work, please leave a comment and I'll try to fix it!如果您发现此代码不起作用的常见系统,请发表评论,我会尽力修复它!

#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);
}

Tested on:测试:

Like everyone else already said: No there isn't.就像其他人已经说过的那样:不,没有。

Also, I would always avoid all the vendor-specific functions here, because when you find that you really need to use them, that's generally a sign that you're doing it wrong.此外,我总是会避免在这里使用所有特定于供应商的功能,因为当您发现确实需要使用它们时,这通常表明您做错了。 You should either store the size separately, or not have to know it at all.您应该单独存储尺寸,或者根本不必知道它。 Using vendor functions is the quickest way to lose one of the main benefits of writing in C, portability.使用供应商函数是失去用 C 编写的主要好处之一,可移植性的最快方法。

I would expect this to be implementation dependent.我希望这取决于实现。
If you got the header data structure, you could cast it back on the pointer and get the size.如果您获得了标头数据结构,则可以将其转换回指针并获得大小。

If you use malloc then you can not get the size.如果使用 malloc,则无法获取大小。

In the other hand, if you use OS API to dynamically allocate memory, like Windows heap functions , then it's possible to do that.另一方面,如果您使用 OS API 动态分配内存,例如 Windows堆函数,则可以这样做。

Well now I know this is not answering your specific question, however thinking outside of the box as it were... It occurs to me you probably do not need to know.好吧,现在我知道这并没有回答您的具体问题,但是跳出框框思考……我想您可能不需要知道。 Ok, ok, no I don't mean your have a bad or un-orthodox implementation... I mean is that you probably (without looking at your code I am only guessing) you prbably only want to know if your data can fit in the allocated memory, if that is the case then this solution might be better.好吧,好吧,不,我不是说你的实现不好或不正统......我的意思是你可能(不看你的代码我只是猜测)你可能只想知道你的数据是否适合在分配的内存中,如果是这种情况,那么这个解决方案可能会更好。 It should not offer too much overhead and will solve your "fitting" problem if that is indeed what you are handling:它不应该提供太多的开销,并且如果这确实是您正在处理的问题,它将解决您的“拟合”问题:

if ( p != (tmp = realloc(p, required_size)) ) p = tmp;

or if you need to maintain the old contents:或者如果您需要维护旧内容:

if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size);

of course you could just use:当然你可以只使用:

p = realloc(p, required_size);

and be done with it.并完成它。

This code will probably work on most Windows installations:此代码可能适用于大多数 Windows 安装:

template <class T>
int get_allocated_bytes(T* ptr)
{
 return *((int*)ptr-4);
}

template <class T>
int get_allocated_elements(T* ptr)
{
 return get_allocated_bytes(ptr)/sizeof(T);
}

Quuxplusone wrote: "Writing a function that can work on any of these void*s impossible, unless you can somehow tell from the pointer's value which of your heaps it came from." Quuxplusone 写道:“编写一个函数来处理这些 void* 中的任何一个都是不可能的,除非你能以某种方式从指针的值中知道它来自你的哪个堆。” Determine size of dynamically allocated memory in C " 确定 C 中动态分配内存的大小

Actually in Windows _msize gives you the allocated memory size from the value of the pointer.实际上在 Windows 中 _msize 为您提供了从指针值分配的内存大小。 If there is no allocated memory at the address an error is thrown.如果地址处没有分配的内存,则会引发错误。

int main()
{
    char* ptr1 = NULL, * ptr2 = NULL;
    size_t bsz;    
    ptr1 = (char*)malloc(10);
    ptr2 = ptr1;
    bsz = _msize(ptr2);
    ptr1++;
    //bsz = _msize(ptr1);   /* error */
    free(ptr2);

    return 0;
}

Thanks for the #define collection.感谢您的#define 集合。 Here is the macro version.这是宏版本。

#define MALLOC(bsz) malloc(bsz)
#define FREE(ptr) do { free(ptr); ptr = NULL; } while(0)
#ifdef __linux__
#include <malloc.h>
#define MSIZE(ptr) malloc_usable_size((void*)ptr)
#elif defined __APPLE__
#include <malloc/malloc.h>
#define MSIZE(ptr) malloc_size(const void *ptr)
#elif defined _WIN32
#include <malloc.h>
#define MSIZE(ptr) _msize(ptr)
#else
#error "unknown system"
#endif

I was struggling recently with visualizing the memory that was available to write to (ie using strcat or strcpy type functions immediately after malloc).我最近正在努力可视化可用于写入的内存(即在 malloc 之后立即使用strcatstrcpy类型函数)。

This is not meant to be a very technical answer, but it could help you while debugging, as much as it helped me.这并不是一个非常技术性的答案,但它可以在调试时为您提供帮助,就像它对我的帮助一样。

You can use the size you malloc d in a memset , set an arbitrary value for the second parameter (so you can recognize it) and use the pointer that you obtained from malloc .您可以在memset使用malloc d 的大小,为第二个参数设置任意值(以便您可以识别它)并使用从malloc获得的指针。

Like so:像这样:

char* my_string = (char*) malloc(custom_size * sizeof(char));
if(my_string) { memset(my_string, 1, custom_size); }

You can then visualize in the debugger how your allocated memory looks like:然后,您可以在调试器中可视化分配的内存是什么样的: 在此处输入图片说明

Note: using _msize only works for memory allocated with calloc , malloc , etc. As stated on the Microsoft Documentation注意:使用_msize仅适用于使用callocmalloc等分配的malloc 。如 Microsoft 文档中所述

The _msize function returns the size, in bytes, of the memory block allocated by a call to calloc , malloc , or realloc . _msize 函数返回调用callocmallocrealloc分配的内存块的大小(以字节为单位)。

And will throw an exception otherwise.否则会抛出异常。

https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize?view=vs-2019 https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize?view=vs-2019

This may work, a small update in your code:这可能有效,您的代码中的一个小更新:

void* inc = (void*) (++p)
size=p-inc;

But this will result 1, that is, memory associated with p if it is char* .但这将导致 1,即与p关联的内存,如果它是char* If it is int* then result will be 4.如果它是int*那么结果将是 4。

There is no way to find out total allocation.没有办法找出总分配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM