当我尝试释放动态分配的指针数组时,Valgrind 抛出无效的 free()

Valgrind throws invalid free() when I try to free an array of dynamically allocated pointers

我正在分配动态分配的指针数组,并尝试在程序结束时释放它们。问题是我总是在 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] = '[=10=]';
    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 说它是一个免费的无效的,但它成功地释放了 argv[0]。

提前谢谢大家!

valgrind 的输出: LINK

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

函数strsep()实际上修改了传递给它的指针,所以char *token = strsep(&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] = '[=10=]';

        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] = '[=11=]';

        argv[i++] = token;

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

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

    free(argv);
}

这消除了很多并发症——据我所知——同样安全。