无法在我的 strtok 程序中正确分配和释放内存

Unable to malloc and free memory properly in my strtok program

所以这是我将一行文件解析为数组 num_args 的程序的一部分,我将其用于进一步的实现:

while(fgets(str, 1024, f) > 0) {
    int id;
    int final_arg_num;
    int my_index;
    char *num_args[100];
    for (id = 0, line = strtok(str, " "); id < 100; id++) {
        if (line == NULL) {
            break;
        }   
        num_args[id] = malloc(16*sizeof(char));
        sscanf(line, "%s", num_args[id]);
        line = strtok(NULL, " "); 
    }   
    final_arg_num = id;

    char *storage_people = (char *)malloc(sizeof(char)*need);
    if (strcmp(num_args[0],"Movie:") != 0) {
        strcpy(storage_people,num_args[0]);
    } else {
        strcpy(storage_people,"");
    }
    for (my_index = 1; my_index < final_arg_num; my_index++) {
        if (strcmp(num_args[0],"Movie:") || (!strcmp(num_args[0],"Movie:") && my_index > 1))
            strcat(storage_people, " " );
        strcat(storage_people, num_args[my_index]);
    }

    if (strcmp(num_args[0],"Movie:") == 0) {
        // do something       
    } else {
        // do something
    }
    /**for (j = 0; j < 100; j++) {
        if (num_args[j] != NULL) {
            free(num_args[j]);
        }
    }**/
    free(storage_people);
}
fclose(f);

如果我不释放 num_args,我会发生内存泄漏;如果我取消注释我程序的 free(num_args[j]) 部分,我会得到这样的 valgrind 错误:

==3062== Conditional jump or move depends on uninitialised value(s)
==3062==    at 0x401D3B: main (original.c:410)
==3062== 
==3062== Conditional jump or move depends on uninitialised value(s)
==3062==    at 0x4C2BDA2: free (in .*)
==3062==    by 0x401D54: main (original.c:411)
==3062== 
==3062== Invalid free() / delete / delete[] / realloc()
==3062==    at 0x4C2BDEC: free (in .*)
==3062==    by 0x401D54: main (original.c:411)
==3062==  Address 0x8 is not stack'd, malloc'd or (recently) free'd

有什么帮助吗?

问题是

char *num_args[100];

声明了一个指针数组,但未初始化。你的 for 循环 解析行不一定设置数组中的所有 100 个空格,所以一些 将保持未初始化状态,很可能 != NULL.

这就是 free 失败的原因,因为在您 free-loop 的某个时刻您正在尝试 为尚未初始化的 num_args[j] 执行 free(num_args[j]) 并且 不是 NULL,因此它崩溃了。

您必须初始化数组,或者像这样 memset

char *num_args[100];
memset(num_args, 0, sizeof num_args);

或使用初始化列表

char *num_args[100] = { NULL };

将所有指针初始化为 null 指针1,2.

你应该像这样fgets检查

while(fgets (str , 1024 , f))

或者像这样

while(fgets (str , 1024 , f) != NULL)

注释

1正如 在评论中指出的,我的语句 将所有指针初始化为 NULL. 是 不完全正确,因为只有第一个元素被初始化为 NULL,所有其他 元素用 0 位模式初始化。可能有架构 其中 NULL 不是由 0 位模式表示的,元素的其余部分 不会指向 NULL。但在大多数体系结构中,NULL 是 0 位模式并且 结果将是所有元素都指向 NULL。看 https://ideone.com/RYAyHm 作为使用 GCC 编译的示例。

2我用的是第二条评论的措辞,解释的很好

您的代码中存在多个问题:

  • 主循环对文件末尾使用虚假测试:您应该改写 while (fgets(str, 1024, f) != NULL) {

  • 释放已分配指针的循环应停止在 id:超出此索引,所有指针都未初始化,因此将它们传递给 free 具有未定义的行为。另请注意,将空指针传递给 free 是完全安全的。如果您这样修改循环,则无需初始化此数组:

    for (j = 0; j < id; j++) {
        free(num_args[j]);
    }
    
  • 将单词存储到数组中的方式既低效又危险:您分配 16 个字节的内存并使用 sscanf()%s 转换说明符来复制wordstrtok 解析。

    • 您应该将要存储到 num_args[id] 中的最大字符数传递给 sscanf(line, "%15s", num_args[id]);
    • 注意,除非源字符串中有其他白色space字符,如\r\n\t...你可以只存储num_args[id] = strdup(line);.
    • 字词
    • 如果这些字符应被视为源字符串中的分隔符,请将它们传递给 strtokline = strtok(str, " \t\r\n\v\f")
  • 将单词复制和连接到 storage_people 中也是有问题的:您没有测试 need 字节是否足够 space 作为结果字符串,包括终止空字节。您应该使用效用函数连接 2 个字符串并重新分配到适当的大小。