4. 更深入的了解

4.1. 为什么需要 Valgrind?

如上所述,内存管理容易出现难以检测的错误。常见的错误可以列举如下:

  1. 使用未初始化的内存

  2. 在内存释放后读取/写入内存

  3. 读取/写入 malloc 分配的块的末尾之外的内存

  4. 读取/写入堆栈上不适当的区域

  5. 内存泄漏 -- 指向 malloc 分配的块的指针永远丢失

  6. malloc/new/new[] 与 free/delete/delete[] 的不匹配使用

  7. POSIX pthreads API 的一些错误用法

这些错误通常会导致崩溃。

这就是我们需要 Valgrind 的情况。 Valgrind 直接与可执行文件一起工作,无需重新编译、重新链接或修改要检查的程序。 Valgrind 决定是否应修改程序以避免内存泄漏,并指出"泄漏"的位置。

Valgrind 模拟程序执行的每条指令。因此,Valgrind 不仅会在您的应用程序中查找错误,还会在所有支持的动态链接(.so 格式)库中查找错误,包括 GNU C 库、X 客户端库、Qt(如果您使用 KDE)等等。这通常包括库,例如 GNU C 库,其中可能包含内存访问违规。

4.2. 用法

4.2.3. 错误类型及示例

Valgrind 实际上只能检测到两种类型的错误:使用非法地址和使用未定义的值。 然而,这足以发现程序中各种内存管理问题。 下面给出一些常见的错误。

4.2.3.2. 非法读取/写入

当您尝试从/向不在程序地址范围内的地址读取/写入时,会发生非法读取/写入错误。

示例程序

#include <stdlib.h>
int main()
{
        int *p, i, a;
        p = malloc(10*sizeof(int));
        p[11] = 1;                /* invalid write error */
        a = p[11];                /* invalid read error */
                free(p);
        return 0;
}

在这里,您尝试从/向未分配给程序的地址 (p+sizeof(int)*11) 读取/写入。

4.2.3.4. 函数的错误匹配使用

在 C++ 中,您可以使用多个函数分配和释放内存,但必须遵守以下规则

示例程序

#include <stdlib.h>
int main()
{
        int *p, i;
        p = ( int* ) malloc(10*sizeof(int));
        for(i = 0;i < 10;i++)
                p[i] = i;
        delete(p);                /* Error: function mismatch */
        return 0;
}

Valgrind 输出是

             ==1066== ERROR SUMMARY: 1 errors from 1 contexts (suppressed:
0 from 0)
             ==1066== malloc/free: in use at exit: 0 bytes in 0 blocks.
             ==1066== malloc/free: 1 allocs, 1 frees, 40 bytes allocated.
             ==1066== For a detailed leak analysis,  rerun with:
--leak-check=yes
             ==1066== For counts of detected errors, rerun with: -v

>从上面的"ERROR SUMMARY"可以清楚地看出,退出时有 0 个块中使用了 0 个字节,这意味着 malloc 的内容已被 freed 通过delete. 因此,这不是 Linux 中的问题,但该程序可能在其他平台上崩溃。

4.3. Valgrind 的限制和依赖性。

没有软件是没有任何限制的。 Valgrind 也是如此,但是大多数程序都可以正常工作。 限制如下:

  1. 程序运行速度慢 25 到 50 倍。

  2. 增加内存消耗。

  3. 高度优化的代码(使用 -O1、-O2 选项编译)有时可能会欺骗 Valgrind。

  4. Valgrind 依赖于动态链接机制。

Valgrind 与 CPU、操作系统以及在较小程度上,编译器和基本 C 库的详细信息密切相关。 目前,Valgrind 仅在 x86 上 Linux 平台(内核 2.2.X 或 2.4.X)上工作。 Valgrind 还需要 Glibc 2.1.X 或 2.2.X。