【问题标题】:Valgrind throws invalid free() when I try to free an array of dynamically allocated pointers当我尝试释放动态分配的指针数组时,Valgrind 抛出无效的 free()
【发布时间】:2019-12-05 21:13:56
【问题描述】:

我正在分配一个动态分配的指针数组,并尝试在程序结束时释放它们。问题是我总是在 valgrind 上收到“invalid free()”错误,尽管我真的找不到问题所在。 例如:我使用 malloc 为 argv[0] 和 argv1 分配内存,然后尝试在 for 循环中释放它们。 我使用以下方法分配指针数组:

char ucom[10], bin[15] = "/bin/";
char* str = (char *) malloc(MAX_LINE_LENGTH*sizeof(char));
char** argv = (char **) malloc(sizeof(char*)); //ALLOCATING MEMORY FOR ARGV
int status, i = 0;
printf("Shell2$**");
strcpy(bin, "/bin/");
fgets(str, MAX_LINE_LENGTH, stdin); 
char *token;
token = strsep(&str, " ");
while(token != NULL && i < 4){
    if(token[strlen(token)-1] == '\n')
        token[strlen(token)-1] = '\0';
    argv[i] = (char *) malloc(MAX_ARGUMENT_LENGTH*sizeof(char)); //ALLOCATING MEMORY FOR POINTERS INSIDE ARGV, running two times in my example
    printf("\n\nI:%d\n\n",i);
    if(argv[i] == NULL) printf("Memory Allocation Problem");
    argv[i] = token;    
    token = strsep(&str, " ");
    i++;
    argv = (char **)realloc(argv, (i+2)*sizeof(char*));
}

然后我尝试释放它们:

wait(&status);
for(int f = 0; f < i; f++){
    if(argv[f] != NULL)
        free(argv[f]); //Free runs two times as the number of time malloc has been called, but fails at the second free.
}
free(str);
free(argv);

尽管 malloc 在我的示例中运行了 2 次,为 argv[0] 和 argv1 分配了内存,但当最后的 for 循环尝试释放 argv1 时,它失败并且 valgrind 说这是free 无效,但它成功释放了 argv[0]。

提前谢谢大家!

valgrind 的输出: LINK

【问题讨论】:

  • 请提供minimal verifiable example。尝试对不完整的代码进行评论是没有效率的,因为问题甚至可能不在您展示的部分。例如,分配代码是否在函数内部?一个常见的错误是将内存分配给本地范围变量并在调用者中释放。但是我们无法从您不完整的代码中判断是否是这种情况。因此,为什么我们总是要求一个最小的可验证示例。
  • argv[i] = token; -- 哎呀,你刚刚破坏了你之前做了两行的内存分配。也许你的意思是strcpy(argv[i], token);
  • 您的 argv 数组以一个元素开始,并且每个循环扩展一个指针,因此它总是比参数多一个(这是合理的),但最终指针不是无效。在while循环之后推荐argv[i] = NULL;
  • 能否请您编辑并减少不必要的缩进?它将提高可读性,特别是对于那些通过移动设备阅读的人(比如我)。
  • 您需要将 Valgrind 的输出 作为文本 包含在问题中

标签: c arrays unix malloc free


【解决方案1】:

除了我和@FredLarson 已经指出的内容之外,还有更多错误。

函数strsep()实际上修改了交给它的指针,所以char *token = strsep(&amp;str, " ")改变指针。由于str是从malloc获取的,所以无法正常释放。

如果你想在 malloc 的内存上调用strsep(),你必须保存原始指针的副本并在最后释放它。

此外,非严格可移植但高度可用的函数strdup() 对复制字符串非常有帮助,因此与其分配一堆内存然后将字符串复制到其中,您只需strdup() 即可字符串(稍后释放)。

#define _BSD_SOURCE // for strsep
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_LINE_LENGTH     80
#define MAX_ARGUMENT_LENGTH 10  // made up numbers

int main()
{
    char *str = malloc(MAX_LINE_LENGTH);
    char *str_save = str;
    char **argv = malloc(sizeof(char*)); //ALLOCATING MEMORY FOR ARGV
    int i = 0;

    printf("Shell2$**"); fflush(stdout); // make sure user sees the prompt
    strcpy(bin, "/bin/");
    fgets(str, MAX_LINE_LENGTH, stdin);

    char *token;

    while ( (token = strsep(&str, " ")) != NULL  &&  i < 4 )
    {
        if(token[strlen(token)-1] == '\n')
            token[strlen(token)-1] = '\0';

        argv[i] = strdup(token);

        if(argv[i] == NULL) printf("Memory Allocation Problem");
        i++;
        argv = realloc(argv, i * sizeof(char*));
    }
    argv[i] = NULL; // GOOD IDEA TO ADD THIS

    // run the shell
    // wait(&status);

    for(int f = 0; f < i; f++){
        if(argv[f] != NULL)
            free(argv[f]);
    }
    free(str_save);
    free(argv);
}

这基本上和你的一样,但实际上你可能甚至不需要为输入行分配内存。为什么不只定义一个本地缓冲区?然后你不必为行缓冲区分配或令牌,因为它们都存在于linebuffer

#define _BSD_SOURCE // for strsep
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_LINE_LENGTH     80

int main()
{
    char **argv = malloc(sizeof(char*)); //ALLOCATING MEMORY FOR ARGV
    int i = 0;

    printf("Shell2$**"); fflush(stdout); // make sure user sees the prompt

    char linebuf[MAX_LINE_LENGTH];
    fgets(linebuf, sizeof linebuf, stdin);

    char *token;
    char *str = linebuf;

    while ( (token = strsep(&str, " ")) != NULL  &&  i < 4 )
    {
        if (token[strlen(token)-1] == '\n')
            token[strlen(token)-1] = '\0';

        argv[i++] = token;

        argv = realloc(argv, i * sizeof(char*));
    }
    argv[i] = NULL; // GOOD IDEA TO ADD THIS

    // run the shell
    // wait(&status);

    free(argv);
}

这消除了很多并发症——据我所知——安全性丝毫不减。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 2019-11-30
    • 1970-01-01
    • 1970-01-01
    • 2021-11-05
    • 2014-02-23
    • 1970-01-01
    相关资源
    最近更新 更多