12

可能的重复:
如何从 C 中的指针获取数组的大小?
有没有办法以编程方式确定 C++ 数组的大小?如果不是,为什么?

我从 C 风格的函数中获得了一个指向一块已分配内存的指针。现在,为了调试目的,知道这个指针指向的分配内存块有多大是非常有趣的。

有什么比盲目地越界引发异常更优雅的呢?

提前致谢, 安德烈亚斯

编辑:

我在 Windows 上使用 VC++2005,在 Linux 上使用 GCC 4.3

编辑2:

_msize在 VC++2005 下不幸的是它在调试模式下导致异常......

编辑3:

好。我已经尝试了我上面描述的方式,除了例外,它有效。至少在我调试并确保在调用库后立即退出缓冲区边界时。奇迹般有效。

它只是不优雅,也不能用于生产代码。

4

9 回答 9

23

这不是标准的,但如果您的库有一个msize()可以为您提供大小的功能。

一个常见的解决方案是使用您自己的函数进行包装malloc,该函数记录每个请求以及大小和产生的内存范围,在发布版本中,您可以切换回 'real' malloc

于 2009-07-30T19:04:38.873 回答
15

如果您不介意为了调试而使用低俗的暴力,您可以使用#define 宏来挂钩对 malloc 和 free 的调用,并用大小填充前 4 个字节。

顺其自然

void *malloc_hook(size_t size) {
    size += sizeof (size_t);
    void *ptr = malloc(size);
    *(size_t *) ptr = size;
    return ((size_t *) ptr) + 1;
}

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

size_t report_size(ptr) {
    return * (((size_t *) ptr) - 1);
}

然后

#define malloc(x) malloc_hook(x)

等等

于 2009-07-30T19:17:24.137 回答
10

C 运行时库不提供这样的功能。此外,故意引发异常也不会告诉您该块有多大。

通常在 C 中解决这个问题的方法是维护一个单独的变量来跟踪分配的块的大小。当然,这有时很不方便,但通常没有其他方法可以知道。

你的 C 运行时库可能会提供一些堆调试函数来查询分配的块(毕竟,free()需要知道块有多大),但任何此类事情都是不可移植的。

于 2009-07-30T19:04:13.243 回答
5

使用gccGNU linker,您可以轻松包装malloc

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


void* __real_malloc(size_t sz);
void* __wrap_malloc(size_t sz)
{
    void *ptr;

    ptr = __real_malloc(sz);
    fprintf(stderr, "malloc of size %d yields pointer %p\n", sz, ptr);

    /* if you wish to save the pointer and the size to a data structure, 
       then remember to add wrap code for calloc, realloc and free */

    return ptr;
}

int main()
{
    char *x;
    x = malloc(103);

    return 0;
}

并编译

gcc a.c -o a -Wall -Werror -Wl,--wrap=malloc

(当然,这也适用于用 g++ 编译的 c++ 代码,如果你愿意的话,也可以使用 new 运算符(通过它的错位名称)。)

实际上,静态/动态加载的库也将使用您的__wrap_malloc.

于 2009-07-30T19:22:43.690 回答
4

不,您不能在超出其边界时依赖异常,除非它在您的实现文档中。这是您编写程序时真正不需要知道的东西的一部分。如果您真的想知道,请深入研究编译器的文档或源代码。

于 2009-07-30T19:04:13.257 回答
4

没有标准的 C 函数可以做到这一点。根据您的平台,可能有一种不可移植的方法——您使用的是什么操作系统和 C 库?

请注意,引发异常是不可靠的 - 在您拥有的块之后可能立即有其他分配,因此您可能直到超过当前块的限制很久之后才会获得异常。

于 2009-07-30T19:04:51.807 回答
4

Valgrind 的 memcheckGoogle 的 TCMalloc (堆检查器部分)等内存检查器会跟踪这类事情。

您可以使用 TCMalloc 转储显示分配位置的堆配置文件,或者您可以使用SameHeap()让它检查以确保您的堆在程序执行的两个点相同。

于 2009-07-30T19:13:27.593 回答
2

部分解决方案:在 Windows 上,您可以使用PageHeap来捕获分配块之外的内存访问。

PageHeap 是存在于 Windows 内核中的备用内存管理器(在 NT 变体中,但现在没有人应该使用任何其他版本)。它获取进程中的每个分配并返回一个内存块,该内存块的结尾与内存页面的结尾对齐,然后它使下一个页面不可访问(无读取,无写入访问)。如果程序试图读取或写入超过块的末尾,您将遇到访问冲突,您可以使用您最喜欢的调试器捕获。

如何获得它:从 Microsoft 下载并安装软件包 Debugging Tools for Windows:http: //www.microsoft.com/whdc/devtools/debugging/default.mspx

然后启动 GFlags 实用程序,转到第 3 个选项卡并输入可执行文件的名称,然后按 键。选中 PageHeap 复选框,单击 OK,一切顺利。

最后一件事:当您完成调试时,不要忘记再次启动 GFlags,并禁用应用程序的 PageHeap。GFlags 将此设置输入到注册表中(在 HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ 下),因此它是持久的,即使在重新启动后也是如此。

另外,请注意,使用 PageHeap 会极大地增加应用程序的内存需求。

于 2009-07-30T20:12:50.063 回答
1

做你想做的事的方法成为分配者。如果您过滤所有请求,然后将它们记录下来以进行调试,那么您可以在内存空闲时找出您想要的内容。

此外,您可以在程序结束时检查是否所有分配的块都已释放,如果没有,请列出它们。此类雄心勃勃的库甚至可以通过宏获取FUNCTIONLINE参数,让您确切知道内存泄漏的位置。

最后,Microsoft 的 MSVCRT 提供了一个可调试堆,其中包含许多有用的工具,您可以在调试版本中使用这些工具来查找内存问题:http: //msdn.microsoft.com/en-us/library/bebs9zyz.aspx

在 Linux 上,您可以使用 valgrind 查找许多错误。http://valgrind.org/

于 2009-07-30T19:07:42.813 回答