在我的strtok程序中无法正确使用malloc和free内存

时间:2018-04-08 02:39:21

标签: c memory valgrind

这是我的程序的一部分,将一行文件解析为数组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

任何帮助?

2 个答案:

答案 0 :(得分:1)

问题在于

char *num_args[100];

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

这就是为什么free失败的原因,因为在你的自由循环中的某些时候你正在尝试 为尚未初始化的free(num_args[j])执行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)

<强> Fotenotes

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

2 我正在使用chux第二条评论所使用的措辞,解释非常好。

答案 1 :(得分:1)

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

  • 主循环使用虚假测试来结束文件:你应该写while (fgets(str, 1024, f) != NULL) {

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

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

    • 您应该使用num_args[id]将最大数量的字符存储到sscanf(line, "%15s", num_args[id]);
    • 请注意,除非源字符串中有其他空白字符,例如\r\n\t ...您只需将该字词存储为{{1} }。
    • 如果要将这些字符视为源字符串中的分隔符,请将其传递给num_args[id] = strdup(line);strtok
  • 将单词复制并连接到line = strtok(str, " \t\r\n\v\f")也是有问题的:您不测试storage_people字节是否足够用于生成的字符串,包括终止空字节。您应该使用实用程序函数将2个字符串与重新分配连接到适当的大小。