C语言中是否有办法找出动态分配的内存大小?

例如,在
之后
char* p = malloc (100);


是否可以找出与p相关的内存大小?

评论

sizeof(char)*…是多余的,因为char的大小保证为1。

@ mk12仍然可以更清楚地了解发生了什么。尤其是当编写为malloc(100 * sizeof(char))时,它遵循在数量的右侧放置单位的通常约定。

实际上,我现在更喜欢编写TYPE * ptr = malloc(100 * sizeof * ptr),其中TYPE仅写入一次。即使更改TYPE,也可以保证得到100个元素的数组。

#1 楼

comp.lang.c常见问题清单·问题7.27-

问。因此,我可以查询malloc软件包以找出分配的块有多大吗?

A。不幸的是,没有标准或可移植的方法。 (某些编译器提供了非标准扩展。)如果您需要知道,则必须自己对其进行跟踪。 (另请参阅问题7.28。)

#2 楼

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




_msize在Windows上

malloc_size在MacOS上

malloc_usable_size在具有glibc的系统上

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

#3 楼

C的思想是为程序员提供工具,以帮助他完成工作,而不是提供更改其工作性质的抽象方法。 C也会尝试避免使事情变得更容易/更安全(如果这样做会以牺牲性能为代价)。

您可能希望对内存区域进行的某些操作只需要确定内存的开始位置即可。地区。这样的事情包括处理以null终止的字符串,处理区域的前n个字节(如果已知该区域至少这么大)等等。

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

许多库函数(例如fread())都需要一个指向区域开始的指针,并且还有这个区域的大小。如果需要区域的大小,则必须对其进行跟踪。

是的,malloc()实现通常可以跟踪区域的大小,但是它们可以间接执行此操作,也可以四舍五入为一个整数。一些价值,或者根本不保留它。即使他们支持它,与您自己跟踪它的大小相比,用这种方法查找大小可能也会比较慢。

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

评论


虽然这个答案不能完全回答问题,但我很欣赏对于这种事情不存在的理性的解释。

–CoatedMoose
2013年9月24日,下午3:34

如果编译器没有任何记录它分配了多大的块,那么在匹配时它会做什么?

–安德鲁·拉撒路(Andrew Lazarus)
15年2月13日在18:19

@AndrewLazarus库实现可能有一种间接的方式来确定它,而不直接添加任何内容到该块中,并且它可能拒绝共享它:)

–库巴没有忘记莫妮卡
15年8月16日在7:53

#4 楼

不,C运行时库不提供此类功能。

某些库可能提供特定于平台或编译器的功能,这些功能可以获取此信息,但是通常可以通过这种方式来跟踪此信息。另一个整数变量。

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


这种方法相对于具有大小和指针的结构的优势

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


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


评论


您还应该重新定义重新分配

–user1251840
18年3月8日在9:40

此答案假定&ret [1]已“适当对齐,以便可以将其分配给具有基本对齐要求的任何类型的对象的指针,然后将其用于访问分配的空间中的此类对象或此类对象的数组” 。可能不是。

–安德鲁·亨利(Andrew Henle)
19-10-31在13:47



#6 楼

就像其他所有人已经说过的:没有。

此外,我总是避免在这里使用所有特定于供应商的功能,因为当您发现确实需要使用它们时,通常这是一个信号你做错了。您应该单独存储大小,或者根本不必知道它。使用供应商函数是丧失使用C语言编写的主要好处之一的可移植性的最快方法。

评论


我看到了一些项目,但是只有一个项目,其中出现了“应该在最初设计的项目之外的其他项目上运行”的想法。公司的每个相关项目都必须从那时起重写。

– Kobor42
13年5月23日在9:52

那你真幸运。我从事许多遗留项目,而这些问题往往很难解决。

– Enno
10月26日9:48

#7 楼

每个人都告诉您这是不可能的,从技术上来说是正确的(最好的正确选择)。出于工程原因,依靠malloc子系统准确地告诉您分配的块的大小是一个坏主意。为了使自己相信这一点,请想象您正在编写一个具有多个不同内存分配器的大型应用程序-也许一部分使用Raw libc malloc,另一部分使用C ++ operator new,然后在另一部分使用某些特定的Windows API。因此,您到处都有各种各样的void*。编写一个可以对所有这些void*都不可能起作用的函数,除非您能以某种方式从指针的值中得知它来自哪个堆。

因此,您可能希望将每个指针包装在您的指针中带有一些约定的程序,该约定指示指针从何而来(以及需要将指针返回到何处)。例如,在C ++中,我们称std::unique_ptr<void>(对于需要为operator delete'd的指针)或std::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;
}


获取非null指针后面的分配大小,该指针直接从libc malloc返回-不是从自定义堆返回,也不是指向到对象的中间-您可以使用以下特定于OS的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);
}


测试于:


Visual Studio,Win64 — _msize

GCC / Clang,glibc,Linux — malloc_usable_size

Clang,libc,Mac OS X — malloc_size

Clang,jemalloc,Mac OS X –可以在实践中使用,但我不相信(将jemalloc的malloc和本机libc的malloc_size混合使用)
在Linux上可以与jemalloc一起正常工作

如果在没有USE_DL_PREFIX的情况下进行编译,则可以在Linux上与dlmalloc一起正常工作

在所有地方都可以与tcmalloc一起正常工作



#8 楼

我希望这取决于实现。
如果您获得标头数据结构,则可以将其强制转换回指针并获取大小。

评论


哪个标题数据结构?

–否
09年8月15日在14:08

@nos,如果一个实现确定了所需的大小以及一个mem-manager头,则返回该头之后的位置作为大小分配的内存块。这样的标题可能会存储大小本身。如果标题typedef是已知的,则可以将分配的指针移回以访问标题及其字段。在这样的实现中,存储器管理器本身将同样实现自由操作,以实现低记帐维护(和高风险)。

– nik
09年8月15日在14:53

根据内存管理器如何实现释放操作的考虑,从指针中得出大小可能是(或可能)不可行(但是必须了解实现方法)。但是,某些实现可以选择在api中提供该原语(在这种情况下,将不需要内部知识)。

– nik
2009年8月15日14:57

#9 楼

如果使用malloc,则无法获取大小。
另一方面,如果使用OS API动态分配内存(如Windows堆函数),则可以做到这一点。

#10 楼

好吧,现在我知道这并不能回答您的特定问题,但是您可以跳出框框思考……我发现您可能不需要知道。好吧,好吧,不,我不是说您的实现错误或不合常规...我的意思是,您可能(不看您的代码而只是猜测)您可能只是想知道您的数据是否适合如果是这种情况,则此方法可能更好。如果确实需要处理,它应该不会提供太多开销,并且可以解决您的“拟合”问题:

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


,或者如果您需要维护旧内容:

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


当然,您可以使用:

p = realloc(p, required_size);


并完成它。 />

#11 楼

此代码可能适用于大多数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);
}


评论


注意:这是C问题的C ++答案。

–chux-恢复莫妮卡
2014年2月11日在21:04

#12 楼

这可能会起作用,在您的代码中进行小幅更新:如果是p,则结果将为4。

无法找出总分配。

#13 楼

Quuxplusone写道:“编写一个可以在任何这些void *上运行的函数都是不可能的,除非您能以某种方式从指针的值中得知它来自哪个堆。”
确定C中动态分配的内存的大小”

实际上,在Windows中,_msize会根据指针的值为您分配分配的内存大小。如果该地址上没有分配的内存,则会引发错误。

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


感谢#define集合。这是宏版本。

#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


#14 楼

最近,我在可视化可写的内存(例如在malloc之后立即使用strcatstrcpy类型的函数)方面苦苦挣扎。

这不是一个非常技术性的答案,但是它可以帮助您调试时对您有帮助。

您可以在malloc中使用您的大小,为第二个参数设置一个任意值(以便您可以识别它)并使用指针您从memset获得的结果。

像这样:

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


然后您可以在调试器中可视化分配的内存的外观:


评论


这只能保证在char *指针上实际起作用。任何其他类型都有对齐问题和不确定行为的风险。

–安德鲁·亨利(Andrew Henle)
19-10-31在13:49

是的,我同意这一点。

– bem22
19-10-31在18:02

#15 楼

注意:使用_msize仅适用于用callocmalloc等分配的内存。如Microsoft文档所述


_msize函数以字节为单位返回内存块的大小
由对callocmallocrealloc的调用分配。


,否则将引发异常。

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