【问题标题】:invalid read of size 1 from valgrind从 valgrind 读取大小为 1 的无效
【发布时间】:2016-07-21 23:47:44
【问题描述】:

在我修复了内存泄漏之后,valgrind 向我显示了一堆如下的行错误,我不知道如何修复它。是因为我释放的空间比我需要的多吗?

第 39 行:

root = importTree(*(argv+1));

第 72 行:

 printf("Result found for %d:\n       city: %s\n       state:%s\n", zip, new->city, new->state);

Node *importTree(char *filename) {
    Node *root = NULL;
    FILE *fp = fopen(filename, "r");

    if (!fp) {
        printf("Error opening file.\n");
        return NULL;
    }

    while (!feof(fp)) {
        Node *new = malloc(sizeof(Node));
        if (!new) {
            printf("Failed to allocate memory. Ending read.\n");
            exit(1);
            fclose(fp);
        }
        new->city = malloc(sizeof(char) * MAXCITYNAME);
        if (!(new->city)) {
            printf("Failed to allocate memory. Ending read.\n");
            exit(1);
            fclose(fp);
        }
        new->left = NULL;
        new->right = NULL;
        char *line = malloc(sizeof(char) * MAXLINELENGTH);
        if (!line) {
            printf("Failed to allocate memory. Ending read.\n");
            exit(1);
            fclose(fp);
        }
        if (fgets(line, MAXLINELENGTH, fp) == NULL) {
            if (!feof(fp)) {
                printf("File reading ended prematurely. Check for errors in the file.\n");
                exit(1);
                fclose(fp);
            }
            free(new->city);
            free(line);
            free(new);
            fclose(fp);
            break;
        }
        char *tmp = strtok(line, ",");
        new->zipCode = atoi(tmp);
        tmp = strtok(NULL, ",");
        strcpy(new->city, tmp);
        new->city[strlen(tmp) + 1] = '\0';
        tmp = strtok(NULL, ",");
        strcpy(new->state, tmp);
        new->state[2] = '\0';
        root = addNode(root, new);
        if (!root) {
            printf("Root of tree is still NULL! Ending read.\n");
            exit(1);
        }
        free(line);
        free(new->city); \\this is the line 220
    }
    fclose(fp);
    return root;
}

这是 valgrind 的输出:

Invalid read of size 1
==7879==    at 0x517FAB4: vfprintf (vfprintf.c:1635)
==7879==    by 0x5188C98: printf (printf.c:34)
==7879==    by 0x400BBD: main (hw3.c:72)
==7879==  Address 0x5657a30 is 0 bytes inside a block of size 30 free'd
==7879==    at 0x4C2AD17: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==7879==    by 0x40103F: importTree (hw3.c:220)
==7879==    by 0x400A31: main (hw3.c:39)
==7879==
==7879== Invalid read of size 1
==7879==    at 0x51ADA99: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1342)
==7879==    by 0x517FA6C: vfprintf (vfprintf.c:1635)
==7879==    by 0x5188C98: printf (printf.c:34)
==7879==    by 0x400BBD: main (hw3.c:72)
==7879==  Address 0x5657a37 is 7 bytes inside a block of size 30 free'd
==7879==    at 0x4C2AD17: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==7879==    by 0x40103F: importTree (hw3.c:220)
==7879==    by 0x400A31: main (hw3.c:39)
==7879==
==7879== Invalid read of size 1
==7879==    at 0x51ADAAC: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1342)
==7879==    by 0x517FA6C: vfprintf (vfprintf.c:1635)
==7879==    by 0x5188C98: printf (printf.c:34)
==7879==    by 0x400BBD: main (hw3.c:72)
==7879==  Address 0x5657a36 is 6 bytes inside a block of size 30 free'd

【问题讨论】:

  • 不要使用sizeof(char),它与 1 没有什么不同。所以它是多余的,只会使代码更难阅读,并且看起来比必要的行长。而且你确实不需要malloc(),因为MAXLINESIZE 使用数组,malloc() 很慢,你真的不需要它。并且测试文件结尾也是多余的,您已经检查了fgets() 是否返回NULL
  • Also while (!feof(file)) is always wrong。您也可以fclose() 该文件两次。在明显担心 valgrind 之前,您需要检查很多事情。
  • @chqrlie:谢谢你添加空格,代码真的让我很难过。
  • 哦,fclose(file)exit(1) 之后根本不起作用。你知道exit(1) 会做什么吗?
  • @iharob:不,它不会使代码更难阅读。数组分配的公认习语是malloc(N * sizeof element)。每次分配数组时都遵循它,即使您知道 sizeof element 保证为 1。偏离此模式会使代码更难阅读,因为它会使读者绊倒并花时间找出为什么省略 sizeof element .虽然很容易弄清楚,但它仍然无关紧要,不值得花时间。

标签: c memory-leaks valgrind


【解决方案1】:

在树中插入节点后,为什么freecity 结构的city 成员?它属于结构,释放树时会被释放两次。

您的代码中还有其他问题:

  • while (!feof(fp)) 总是错的。在您的情况下,只需使用 for(;;) 永远运行,并在 fgets() 在文件末尾失败时优雅地退出循环。
  • 您不应该使用new 作为标识符,因为它是C++ 中的关键字,它会混淆代码着色器(和阅读器)。就叫它nodenp
  • new->city[strlen(tmp) + 1] = '\0'; 行充其量是无用的,并且可能导致缓冲区溢出。
  • 确实,我们不知道您的数组大小的值,但是您在调用strcpy() 复制行块之前没有检查大小。这可能会引发未定义的行为。
  • 您应该改用strdup() 为行片段分配正确的大小并通过一个简单的步骤复制内容。
  • 同样,成功的fgets() 之后分配节点将简化错误情况。
  • 您没有检查strtok() 的返回值。无效的文件内容可能会导致返回值为NULL,从而调用未定义的行为而不是正常的致命错误。

这是一个更简单的修正版:

Node *importTree(const char *filename) {
    char line[MAXLINELENGTH];
    Node *root = NULL;
    FILE *fp = fopen(filename, "r");

    if (!fp) {
        printf("Error opening file.\n");
        return NULL;
    }

    while (fgets(line, MAXLINELENGTH, fp) != NULL) {
        Node *node = malloc(sizeof(Node));
        if (!node) {
            printf("Failed to allocate memory. Ending read.\n");
            fclose(fp);
            exit(1);
        }
        new->left = NULL;
        new->right = NULL;
        char *tmp = strtok(line, ",");
        if (!tmp) {
            printf("Invalid file contents. Ending read.\n");
            fclose(fp);
            exit(1);
        }
        new->zipCode = atoi(tmp);
        tmp = strtok(NULL, ",");
        if (!tmp) {
            printf("Invalid file contents. Ending read.\n");
            fclose(fp);
            exit(1);
        }
        node->city = strdup(tmp);
        if (!(new->city)) {
            printf("Failed to allocate memory. Ending read.\n");
            fclose(fp);
            exit(1);
        }
        tmp = strtok(NULL, ",");
        if (!tmp) {
            printf("Invalid file contents. Ending read.\n");
            fclose(fp);
            exit(1);
        }
        tmp[2] = '\0';
        node->state = strdup(tmp);
        if (!node->state) {
            printf("Failed to allocate memory. Ending read.\n");
            fclose(fp);
            exit(1);
        }
        root = addNode(root, node);
        if (!root) {
            printf("Root of tree is still NULL! Ending read.\n");
            fclose(fp);
            exit(1);
        }
    }
    if (!feof(fp)) {
        printf("File reading ended prematurely. Check for errors in the file.\n");
        fclose(fp);
        exit(1);
    }
    fclose(fp);
    return root;
}

【讨论】:

  • 我希望这是唯一的问题,代码中的问题太多了。
  • new的评论不错,我以前用过。但这肯定是个坏主意。
  • 如果我不释放它,它实际上给了我一个内存泄漏。
  • 泄漏摘要:==9870== 肯定丢失:5,000 个块中的 150,000 个字节 ==9870== 间接丢失:0 个块中的 0 个字节 ==9870== 可能丢失:0 个块中的 0 个字节==9870== 仍然可以访问:0 个块中的 0 个字节 ==9870== 抑制:0 个块中的 0 个字节
  • @fjidsanfklxingdwsf 您确实需要释放它——但在释放树节点时释放它,而不是在插入它们时释放它。你只是在错误的时间和地点释放它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 2017-11-17
  • 2020-09-30
  • 2014-11-30
  • 1970-01-01
  • 1970-01-01
  • 2013-02-13
相关资源
最近更新 更多