在 C 中释放 malloc 字符串的二维数组失败
Freeing a 2D array of malloc'd strings fails in C
我知道这是一个经常被问到的问题,但我已经阅读了 10 多个封闭的问题,但没有任何运气,因为我的解决方案似乎与其他人提出的解决方案相匹配。
我正在编写自己的 shell 作为学习练习,在这样做的过程中,我正在 malloc'ing 一个字符串数组作为程序调用的参数。我的 malloc 完成如下,其中 argcnt 是给定的参数数量 + 2 以匹配标准 argv 大小并允许数组以 null 终止并因此被 execvp 使用:
char ** args;
args = (char **) malloc(argcnt*sizeof(char *));
for(i = 0; i < argcnt; i++) {
args[i] = (char *) malloc(80*sizeof(char));
printf("Made %d\n", i);
}
然后我释放相同的内存如下:
for (i = 0; i < argcnt; i++) {
free(args[i]);
printf("Freed %d\n", i);
}
free(args);
程序编译但在运行时无法释放 argv[1]。 运行程序然后调用ls -a -l
的示例输出如下:
jack@ubuntu:~/myshell$ ./myshell
[30/03 23:34] # ls -a -l
Argcnt: 4
Made 0
Made 1
Made 2
Made 3
Arg[0]: ls
Arg[1]: -a
Arg[2]: -l
Arg[3]: (null)
Freed 0
*** Error in `./myshell': free(): invalid pointer: 0x0000000001a84c43 ***
Aborted (core dumped)
过去 2 小时我一直在努力解决这个问题,无法找出问题所在,因此非常感谢对问题的一些见解。
编辑:
当前破坏我程序的函数是:
void breakargs(char * cmd, char ** args) {
char * tok = malloc(strlen(cmd)*sizeof(char)); //maximum token size is full cmd
char * str = malloc(strlen(cmd)*sizeof(char));
int i=1;
strcpy(str, cmd); //maintains integrity of cmd
args[0] = tok = strtok(str, " ");
while (tok != NULL)
{
args[i] = tok = strtok(NULL, " ");
i++;
}
args[i] = '[=13=]';
free(tok);
}
第二次编辑:问题是我用 strtok 重新分配了我原来的 malloc 所做的指针,所以原来的指针引用丢失了。此外,我对参数使用确定的字符串长度可能会导致问题。解决方案是当我知道需要存储在那里的字符串的长度时,只 malloc args[i] ,然后使用 strcpy 将字符串移动到该内存位置,而不是像我一直在做的那样直接赋值。这保持了指针引用的完整性并允许它被正确释放。
问题不在您显示的代码中;它在您未显示的代码中。没有阅读完整的评论链,我怀疑你正在做类似的事情:
args[0] = "ls";
args[1] = "-a";
args[2] = "-l";
args[3] = NULL;
然后尝试释放 args
数组。也许您的代码没有那么明目张胆,但最终结果可能大同小异。例如,您可以拆分读取到缓冲区中的命令行,并将指向缓冲区部分的指针复制到 args
数组元素中。 (更新: 这 才是真正的问题。)
这已经消除了分配的内存(你已经丢失了它——你已经泄漏了内存),稍后你试图释放未分配的内存(malloc()
或其中之一未返回的指针它的朋友)。第一个这样的 free()
通常 'works',但完全搞乱了 malloc()
等人使用的控件,因此第二个 free()
失败了。许多(但不是全部)现代系统都有一个 malloc()
可以检测到许多此类滥用行为。
您需要更像这样的代码:
strcpy(args[0], "ls");
strcpy(args[1], "-a");
strcpy(args[2], "-l");
free(args[3]);
args[3] = NULL;
请注意,为每个字符串分配 80 个字节是对这些字符串的过度分配。 OTOH,它也可能完全不适合其他(更长的)论点。您应该像以前一样为 args
本身分配内存;您可能应该根据相应参数包含的内容为各个元素分配内存。有一个名为 strdup()
的(POSIX 但不是标准 C)函数,它可能就是您要查找的内容 — 它为字符串的副本分配足够的 space 并将字符串复制到分配 space。或者,您可能根本不需要分配。
如果它适用于您的平台,您应该使用 valgrind
来验证您的内存使用情况并识别您的滥用行为。
strtok
return 指向最初传递给 strtok
的字符串的指针,无需为该指针分配 space。
当您将 strtok
的 return 值分配给指针变量时,您会用新值覆盖该变量的指针值。这意味着,如果该指针变量指向您已经分配的内存,则该内存将 "leaked" 因为您不再有指向它的指针。
简而言之 - 如果您为指针变量分配内存,请不要为该指针变量分配不同的值,除非您已经 free
d 内存或将指针值放在其他地方。
在您的 breakArgs
函数中存在一些问题:
void breakargs(char * cmd, char ** args) {
char * tok = malloc(strlen(cmd)*sizeof(char)); //maximum token size is full cmd
您不需要为 tok
分配内存,因为您将从 strtok
中为其分配一个值
char * str = malloc(strlen(cmd)*sizeof(char));
int i=1;
strcpy(str, cmd); //maintains integrity of cmd
args[0] = tok = strtok(str, " ");
不要直接分配给 args[0]
,因为那样会覆盖指向您已经分配的内存的指针。而是分配到 tok
,然后是 strcpy(args[0], tok
);
while (tok != NULL)
{
args[i] = tok = strtok(NULL, " ");
不要直接分配给 args[i]
,因为那样会覆盖指向您已经分配的内存的指针。而是分配到 tok
,然后是 strcpy(args[i], tok
);
您还应该在 strcpy
之前检查 tok
是否为 NULL。
i++;
}
args[i] = '[=13=]';
不要直接赋值给 args[i],而是可以用 strcpy(args[i], "")
表示结尾,这样结尾就有一个空字符串。
free(tok);
不要在这里释放 tok
,因为在此阶段 tok
应该为 NULL,不过您确实需要 free (str)
。
}
另请注意其他一些关于检查您处理的参数是否超过内存限制(例如每项 80 个字符)的评论。
我知道这是一个经常被问到的问题,但我已经阅读了 10 多个封闭的问题,但没有任何运气,因为我的解决方案似乎与其他人提出的解决方案相匹配。
我正在编写自己的 shell 作为学习练习,在这样做的过程中,我正在 malloc'ing 一个字符串数组作为程序调用的参数。我的 malloc 完成如下,其中 argcnt 是给定的参数数量 + 2 以匹配标准 argv 大小并允许数组以 null 终止并因此被 execvp 使用:
char ** args;
args = (char **) malloc(argcnt*sizeof(char *));
for(i = 0; i < argcnt; i++) {
args[i] = (char *) malloc(80*sizeof(char));
printf("Made %d\n", i);
}
然后我释放相同的内存如下:
for (i = 0; i < argcnt; i++) {
free(args[i]);
printf("Freed %d\n", i);
}
free(args);
程序编译但在运行时无法释放 argv[1]。 运行程序然后调用ls -a -l
的示例输出如下:
jack@ubuntu:~/myshell$ ./myshell
[30/03 23:34] # ls -a -l
Argcnt: 4
Made 0
Made 1
Made 2
Made 3
Arg[0]: ls
Arg[1]: -a
Arg[2]: -l
Arg[3]: (null)
Freed 0
*** Error in `./myshell': free(): invalid pointer: 0x0000000001a84c43 ***
Aborted (core dumped)
过去 2 小时我一直在努力解决这个问题,无法找出问题所在,因此非常感谢对问题的一些见解。
编辑: 当前破坏我程序的函数是:
void breakargs(char * cmd, char ** args) {
char * tok = malloc(strlen(cmd)*sizeof(char)); //maximum token size is full cmd
char * str = malloc(strlen(cmd)*sizeof(char));
int i=1;
strcpy(str, cmd); //maintains integrity of cmd
args[0] = tok = strtok(str, " ");
while (tok != NULL)
{
args[i] = tok = strtok(NULL, " ");
i++;
}
args[i] = '[=13=]';
free(tok);
}
第二次编辑:问题是我用 strtok 重新分配了我原来的 malloc 所做的指针,所以原来的指针引用丢失了。此外,我对参数使用确定的字符串长度可能会导致问题。解决方案是当我知道需要存储在那里的字符串的长度时,只 malloc args[i] ,然后使用 strcpy 将字符串移动到该内存位置,而不是像我一直在做的那样直接赋值。这保持了指针引用的完整性并允许它被正确释放。
问题不在您显示的代码中;它在您未显示的代码中。没有阅读完整的评论链,我怀疑你正在做类似的事情:
args[0] = "ls";
args[1] = "-a";
args[2] = "-l";
args[3] = NULL;
然后尝试释放 args
数组。也许您的代码没有那么明目张胆,但最终结果可能大同小异。例如,您可以拆分读取到缓冲区中的命令行,并将指向缓冲区部分的指针复制到 args
数组元素中。 (更新: 这
这已经消除了分配的内存(你已经丢失了它——你已经泄漏了内存),稍后你试图释放未分配的内存(malloc()
或其中之一未返回的指针它的朋友)。第一个这样的 free()
通常 'works',但完全搞乱了 malloc()
等人使用的控件,因此第二个 free()
失败了。许多(但不是全部)现代系统都有一个 malloc()
可以检测到许多此类滥用行为。
您需要更像这样的代码:
strcpy(args[0], "ls");
strcpy(args[1], "-a");
strcpy(args[2], "-l");
free(args[3]);
args[3] = NULL;
请注意,为每个字符串分配 80 个字节是对这些字符串的过度分配。 OTOH,它也可能完全不适合其他(更长的)论点。您应该像以前一样为 args
本身分配内存;您可能应该根据相应参数包含的内容为各个元素分配内存。有一个名为 strdup()
的(POSIX 但不是标准 C)函数,它可能就是您要查找的内容 — 它为字符串的副本分配足够的 space 并将字符串复制到分配 space。或者,您可能根本不需要分配。
如果它适用于您的平台,您应该使用 valgrind
来验证您的内存使用情况并识别您的滥用行为。
strtok
return 指向最初传递给 strtok
的字符串的指针,无需为该指针分配 space。
当您将 strtok
的 return 值分配给指针变量时,您会用新值覆盖该变量的指针值。这意味着,如果该指针变量指向您已经分配的内存,则该内存将 "leaked" 因为您不再有指向它的指针。
简而言之 - 如果您为指针变量分配内存,请不要为该指针变量分配不同的值,除非您已经 free
d 内存或将指针值放在其他地方。
在您的 breakArgs
函数中存在一些问题:
void breakargs(char * cmd, char ** args) {
char * tok = malloc(strlen(cmd)*sizeof(char)); //maximum token size is full cmd
您不需要为 tok
分配内存,因为您将从 strtok
char * str = malloc(strlen(cmd)*sizeof(char));
int i=1;
strcpy(str, cmd); //maintains integrity of cmd
args[0] = tok = strtok(str, " ");
不要直接分配给 args[0]
,因为那样会覆盖指向您已经分配的内存的指针。而是分配到 tok
,然后是 strcpy(args[0], tok
);
while (tok != NULL)
{
args[i] = tok = strtok(NULL, " ");
不要直接分配给 args[i]
,因为那样会覆盖指向您已经分配的内存的指针。而是分配到 tok
,然后是 strcpy(args[i], tok
);
您还应该在 strcpy
之前检查 tok
是否为 NULL。
i++;
}
args[i] = '[=13=]';
不要直接赋值给 args[i],而是可以用 strcpy(args[i], "")
表示结尾,这样结尾就有一个空字符串。
free(tok);
不要在这里释放 tok
,因为在此阶段 tok
应该为 NULL,不过您确实需要 free (str)
。
}
另请注意其他一些关于检查您处理的参数是否超过内存限制(例如每项 80 个字符)的评论。