2

我正在搞乱分配和释放内存以掌握它。下面,我从命令行 ( 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 对此有一些问题......

4

2 回答 2

3

释放代码

您的释放代码很有趣——不是完全错误,但也不是完全正确:

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在释放它之前进行了测试,但是您一直在使用它,因此那里的测试是多余的。此外,您可以做到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,并且仅在已知它为非空时才释放它是一个(非常非常)次要的优化。

我怀疑这是否是问题的直接原因(稍后我会更加努力地查看其余代码),但这是需要考虑的事情。

分配代码

你有:

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)在正确的位置获得乘法可能是至关重要的。

ISO/IEC 9899:2011 §6.5.3.4sizeof_Alignof运算符

¶4 当sizeof应用于类型为 , 或 , 的操作数charunsigned char或其 signed 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 运行时库分配。我还没有valgrindMac OS X 10.8 系统可以与之比较。我建议你尝试在下面运行一个非常简单的程序valgrind,看看它告诉你什么:

int main(void) { return 0; }

这将是您系统的基线。如果有泄漏,它们不是由你的代码引起的,你需要学习如何记录抑制,这样你的程序就不会因为 O/S 的违规而受到惩罚。

如果你运行上面的简单程序并发现它不会产生泄漏——或者泄漏更简单、更小——然后尝试一些稍微复杂一些的程序:

#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 不是最新的)。

于 2013-02-24T18:23:04.897 回答
2

我认为你永远不会设置duplicate[0]任何东西,所以当你使用copy[i]in时freeDuplicateArgs,从 i=0 开始,你不知道会发生什么。

于 2013-02-24T18:11:00.490 回答