繁体   English   中英

Valgrind-复制/释放阵列时内存泄漏

[英]Valgrind - Memory Leaks When Duplicating/Freeing Arrays

我在弄乱分配和释放内存以获取更多信息。 下面,我从命令行( char **argv )接收字符串参数,并尝试将它们作为大写字符串复制到另一个malloc数组中:

static char **duplicateArgs(int argc, char **argv) {
    if (argc <= 1) {
        printf("%s\n", "No strings to duplicate.");
        exit(1);
    }
    char **duplicate = (char**)malloc((argc + 1) * sizeof(char**));

    int i = 1;
    int j = 0;
    int stringLength;

    while (argv[i]) {
        stringLength = strlen(argv[i]);
        duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);
        for (j = 0; j < stringLength; j++) {
            duplicate[i][j] = toupper(argv[i][j]);
        }
        duplicate[i][j]= '\0';
        i++;
    }
    duplicate[i] = NULL;
    return duplicate;
}

我也有释放它们的功能:

static void freeDuplicateArgs(char **copy) {
    int i = 0;
    while (copy[i]) {
        if (copy[i] != NULL){
            free(copy[i]);
        }
        i++;
    }
    if (copy != NULL){
        free(copy);
        }
}

main函数中,我调用以下方法,将它们打印出来,然后释放它们:

    char **copy = duplicateArgs(argc, argv);
    char **p = copy;

    argv++;
    p++;
    while(*argv) {
        printf("%s %s\n", *argv++, *p++);
    }

    freeDuplicateArgs(copy);
    return 0;

输出按预期工作,定期打印出字符串,然后大写输出。 但是,当我对Valgrind进行检查时,会遇到以下麻烦:

==20867== Conditional jump or move depends on uninitialised value(s)
==20867==    at 0x100000D1A: freeDuplicateArgs (part2.c:32)
==20867==    by 0x100000E19: main (part2.c:57)
==20867== 
==20867== 
==20867== HEAP SUMMARY:
==20867==     in use at exit: 62,847 bytes in 368 blocks
==20867==   total heap usage: 520 allocs, 152 frees, 66,761 bytes allocated
==20867== 
==20867== LEAK SUMMARY:
==20867==    definitely lost: 8,639 bytes in 18 blocks
==20867==    indirectly lost: 1,168 bytes in 5 blocks
==20867==      possibly lost: 4,925 bytes in 68 blocks
==20867==    still reachable: 48,115 bytes in 277 blocks
==20867==         suppressed: 0 bytes in 0 blocks

如果编译器没有为我的freeDuplicates方法引发任何错误并且我的输出正常,为什么会出现这样的内存混乱情况?

编辑:我还应该提到我在OSX 10.8上-显然Valgrind与此有关。

释放代码

您的释放代码很有趣-并非完全错误,但也并非完全正确:

static void freeDuplicateArgs(char **copy) {
    int i = 0;
    while (copy[i]) {
        if (copy[i] != NULL){
            free(copy[i]);
        }
        i++;
    }
    if (copy != NULL){
        free(copy);
    }
}

您可以在释放copy != NULL之前测试copy != NULL ,但是一直以来都在使用它,因此该测试在那里很多余。 此外,您可以执行free(NULL)而不会遇到问题。

然后我们看一下循环:

 while (copy[i] != NULL)
 {
     if (copy[i] != NULL)

循环条件和if条件相同-同样,您可以free(NULL)

您的while循环具有写出的for循环结构。 您最好在以下方面做得更好:

static void freeDuplicateArgs(char **copy)
{
    if (copy != NULL)
    {
        for (int i = 0; copy[i] != NULL; i++)
            free(copy[i]);
        free(copy);
    }
}

copy != NULL的测试可防止崩溃,并且仅在已知为非null时释放它,这是(非常非常)小的优化。

我怀疑这是问题的直接起因(稍后我会在其余的代码中看起来有点困难),但这是要考虑的问题。

分配代码

你有:

static char **duplicateArgs(int argc, char **argv) {
    if (argc <= 1) {
        printf("%s\n", "No strings to duplicate.");
        exit(1);
    }

首先,可以在没有用户参数的情况下调用程序,但是它仍然具有argv[0] ,程序名称和NULL指针。 您的代码可能应该通过返回(char **)NULL简单地处理负值。 对于0参数,您将返回一个指向空指针的指针。 对于一个或多个参数,需要做的工作。

考虑这一行:

duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);

您可能会考虑两种情况: sizeof(char) == 1sizeof(char) > 1 (是的,请稍等-等待!)。 如果sizeof(char) == 1 ,那么您不需要乘以它。 如果sizeof(char) > 1 ,则说明您没有分配足够的空间。 表达式应为(stringLength + 1) * sizeof(char) 因此,编写以下两行之一:

duplicate[i] = (char*)malloc((stringLength + 1) * sizeof(char));
duplicate[i] = (char*)malloc(stringLength + 1);

我注意到C标准说定义为sizeof(char) == 1 ,所以实际结果是相同的,但是如果您正在处理int或其他sizeof(type)数组,请在右侧获取与sizeof(type)相乘的值这个地方可能很关键。

ISO / IEC 9899:2011§6.5.3.4中sizeof_Alignof运营商

¶4当将sizeof应用于类型为charunsigned charsigned char (或其限定版本)的操作数时,结果为1。

Mac OS X 10.7.5上的Valgrind

以您的代码几乎与问题中的代码相同:

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

static char **duplicateArgs(int argc, char **argv)
{
    if (argc <= 1) {
        printf("%s\n", "No strings to duplicate.");
        exit(1);
    }
    char **duplicate = (char**)malloc((argc + 1) * sizeof(char**));

    int i = 0;
    int j = 0;
    int stringLength;

    while (argv[i]) {
        stringLength = strlen(argv[i]);
        duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);
        for (j = 0; j < stringLength; j++) {
            duplicate[i][j] = toupper(argv[i][j]);
        }
        duplicate[i][j]= '\0';
        i++;
    }
    duplicate[i] = NULL;
    return duplicate;
}

static void freeDuplicateArgs(char **copy)
{
    int i = 0;
    while (copy[i]) {
        if (copy[i] != NULL){
            free(copy[i]);
        }
        i++;
    }
    if (copy != NULL){
        free(copy);
        }
}

int main(int argc, char **argv)
{
    char **copy = duplicateArgs(argc, argv);
    char **p = copy;

    argv++;
    p++;
    while(*argv) {
        printf("%s %s\n", *argv++, *p++);
    }

    freeDuplicateArgs(copy);
    return 0;
}

(区别是在本工作代码中为int i = 0;而不是问题代码中为int i = 1;

valgrind下运行时,这将获得干净的健康单:

$ gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition mlk.c -o mlk
$ valgrind ./mlk nuts oh hazelnuts
==582== Memcheck, a memory error detector
==582== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==582== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==582== Command: ./mlk nuts oh hazelnuts
==582== 
nuts NUTS
oh OH
hazelnuts HAZELNUTS
==582== 
==582== HEAP SUMMARY:
==582==     in use at exit: 18,500 bytes in 33 blocks
==582==   total heap usage: 38 allocs, 5 frees, 18,564 bytes allocated
==582== 
==582== LEAK SUMMARY:
==582==    definitely lost: 0 bytes in 0 blocks
==582==    indirectly lost: 0 bytes in 0 blocks
==582==      possibly lost: 0 bytes in 0 blocks
==582==    still reachable: 18,500 bytes in 33 blocks
==582==         suppressed: 0 bytes in 0 blocks
==582== Rerun with --leak-check=full to see details of leaked memory
==582== 
==582== For counts of detected and suppressed errors, rerun with: -v
==582== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
$

如您所见,没有报告内存错误。 对于该机器,仍在使用的33个块是“正常”的; 它们是由Mac OS XC运行时库分配的。 我还没有在Mac OS X 10.8系统上使用valgrind进行比较。 我建议您尝试在valgrind下运行一个非常简单的程序,看看它能告诉您什么:

int main(void) { return 0; }

这将是您系统的基准。 如果存在泄漏,则泄漏不是由您的代码引起的,并且您将需要学习如何记录抑制,以便您的程序不会因操作系统的违背行为而受到惩罚。

如果您在上面运行了简单程序,发现它不会产生泄漏(或者泄漏从根本上变得越来越小),那么请尝试一些更复杂的程序:

#include <stdlib.h>
int main(void)
{
    void *vp = malloc(32);
    free(vp);
    return(0);
}

这样可以确保将malloc()拖入并使用,但显然不会泄漏(它也可以正确处理malloc()失败!)。

valgrind对Mac OS X 10.8的支持

Valgrind网站说:

2012年9月18日:valgrind-3.8.1,用于X86 / Linux,AMD64 / Linux,ARM / Linux,PPC32 / Linux,PPC64 / Linux,S390X / Linux,MIPS / Linux,ARM / Android(2.3.x及更高版本),提供X86 / Android(4.0和更高版本),X86 / Darwin和AMD64 / Darwin(Mac OS X 10.6和10.7,对10.8的支持有限)。

您所看到的可能是对Mac OS X 10.8的有限支持的一个方面。

我看到我需要为自己重建valgrind (3.7.0不是最新的)。

我认为您永远不会将freeDuplicateArgs duplicate[0]设置为任何值,所以当您在freeDuplicateArgs使用copy[i]时(从i = 0开始),您将不知道会发生什么。

暂无
暂无

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

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