使用C语言中的fgets逐行读取文件,在读取时出现“invalid read of size”错误

4
我在我的代码中一直收到valgrind错误,花费了三个小时也没有头绪,所以我需要你们的帮助。
基本上我只是读取目录中包含的文件并解析它们。这里是我复制的产生错误的最短代码示例:
int main(int argc, char** argv) {

 parse_files_dir("/Users/link_to_dir_example/");
 return (EXIT_SUCCESS);
}

void parse_files_dir(char *dirLink){

int dLink_l =strlen(dirLink);
int max_len = dLink_l*2;

char* full_path=malloc(sizeof(char)*(max_len+1));
//check if null pointer...

strncpy(full_path, dirLink, dLink_l);

DIR *dir;
struct dirent *dir_con;
dir=opendir(dirLink);

if (dir == NULL){
    fprintf(stderr, "Problem opening directory: \"%s\". Aborting...\n", dirLink);
    exit(EXIT_FAILURE);
}

while((dir_con = readdir(dir)) != NULL){

    if (dir_con->d_name[0] == '.') continue;

    if (dLink_l+strlen(dir_con->d_name)>max_len) //realloc full path..

    strncpy(&full_path[dLink_l], dir_con->d_name, strlen(dir_con->d_name));
    full_path[dLink_l+strlen(dir_con->d_name)] = '\0';        

    parse_one_file(full_path);  // (*) <=== valgrind complain 
    full_path[dLink_l] = '\0';
} 
free(full_path);
closedir(dir);
}

现在来讲一下实际问题的解决方法:

void parse_one_file(char* link) {

FILE *file = fopen(link, "r");   
if (file == NULL) //error message

int line_len=0;
int line_max=1000;
char* line= malloc(sizeof(char)*line_max);
line[0] = '\0';

char* line_full= malloc(sizeof(char)*line_max);
line_full[0] = '\0';
int line_full_len = 0;

//check all allocations for null pointers

while(fgets(line, line_max, file) != NULL){   // <=== Here is where valgrind complains !!!!

    line_len = strlen(line);
    if (line[line_len-1] == '\n'){

            strncpy(&line_full[line_full_len], line, line_len);
            line_full_len+=line_len;
    }
    else{

        //Check if line_full has enough memory left   
        strncpy(&line_full[line_full_len], line, line_len);
        line_full_len+=line_len;
    }
    line[0] = '\0';
}
free(line);
free(line_full);
fclose(file);
}

我一直收到这个错误信息:
 ==4929== Invalid read of size 32
 ==4929==    at 0x1003DDC1D: _platform_memchr$VARIANT$Haswell (in       /usr/lib/system/libsystem_platform.dylib)
 ==4929==    by 0x1001CF66A: fgets (in /usr/lib/system/libsystem_c.dylib)
 ==4929==    by 0x100000CD8: parse_one_file (main.c:93)
 ==4929==    by 0x100000B74: parse_files_dir (main.c:67)
 ==4929==    by 0x100000944: main (main.c:28)
 ==4929==  Address 0x100804dc0 is 32 bytes before a block of size 4,096 in arena "client"

我真的不知道我的错误在哪里,我一直在清空缓冲区行,我从未读取超过其分配的字节数。

我注意到有趣的是,如果目录“dirLink”仅有一个文件,则不会出现错误,但如果有两个或更多,则会出现错误,因此我认为错误在于如何生成路径“full_path”,但是然后我用(仅出于测试原因)替换了行“*”

   parse_one_file("another/example/path/");

错误仍然存在...

dLink_l 的计算应该是:int dLink_l = strlen(dirLink) + 1; 其中的 +1 是为了包括字符串末尾的 NUL 字节。 - user3629249
在调用malloc()函数时,表达式sizeof(char)被定义为1。该表达式对传递给malloc()的参数绝对没有影响,并且会使代码混乱。建议删除该表达式。始终检查(!=NULL)从malloc()返回的值以确保操作成功。 - user3629249
当调用系统函数失败时,立即(而不是几行代码后)调用perror(),这样你的错误消息和由errno选取的系统错误消息将会输出到stderr。 - user3629249
关于这种类型的代码行:if (file == NULL) //error message 除了输出错误消息之外,还应该调用return或exit()函数,因为文件未打开,无法从中读取数据。 - user3629249
请注意,为了易读性和人类理解的便利性,请始终缩进代码。建议使用4个空格缩进,因为即使使用可变宽度字体,也足够宽。建议在每个开放括号“{”后缩进,并在每个关闭括号“}”前取消缩进。永远不要使用制表符,因为每个文字处理器/编辑器都有不同的制表位停靠点/制表位宽度设置。 - user3629249
显示剩余5条评论
2个回答

2

除非你的文件总大小小于1000个字节,否则你正在写入超过line_full缓冲区的末尾,该缓冲区总大小仅为1000个字节。这将不可避免地破坏你的内存,并导致类似于fgets中经历的错误。


1
if(line[line_len-1] == '\n'){
  strncpy(&line_full[line_full_len], line, line_len);
  line_full_len+=line_len;
}

这并不完全正确,你只能复制strncpy() (line_max - line_full_len)字节,不能保证可以复制line_len字节。换句话说,从位置line_full[500]开始,你不能再写入1000个字节。
else分支中也存在相同的错误。

我知道那一点,只是在写代码时忘记了加上 "//检查line_full是否有足够的内存" 这部分内容。 - malajedala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接