main()调用多个C文件统计函数仅首个返回正确值的原因及正确调用方式
问题产生的根本原因
- C语言的FILE文件流内置读写位置标记:文件刚打开时,标记停在文件起始位置;所有读操作都会从当前标记位置开始向后读取,每读一字节,标记就向后偏移一位,过程中不会自动回到文件头部。
- 对应代码的执行链路:
- 调用
non_blank_lines(input)时,函数内部的fgets循环会顺序读完整个文件,直到遇到文件结束符(EOF)才退出循环,此时文件流的位置标记已经停在文件最末尾。 - 后续调用
all_lines(input)时,传入的是同一个已经停在末尾的流指针,函数内部的getc从当前位置开始读,第一次读取就遇到EOF,循环体一次都不会执行,最终返回0。
- 调用
常见的正确实现方案
- 方案1:每次调用统计函数前,把文件流位置重置回文件开头
直接调用标准库函数rewind(input)即可完成位置重置,等效操作是fseek(input, 0, SEEK_SET),改动量最小。示例改动后的调用逻辑:
注意:如果后续新增其他统计函数,每调用一个前都要执行一次位置重置。// 统计非空行 printf("The file %s has %d filled lines.\n", file_name, non_blank_lines(input)); // 重置文件流位置到开头 rewind(input); // 统计总行数 printf("The file %s has in total %d lines.\n", file_name, all_lines(input)); - 方案2:单次文件遍历完成所有指标统计
不要让每个统计函数各自独立读一遍文件,而是在同一个读取循环里,同步累计总行数、空行数、非空行数、注释行数等所有需要的指标。这种方式只需要从头到尾读一遍文件,性能更高,从根源上避免了流位置冲突,是更合理的工程实现方式。 - 方案3:每个统计函数独立打开专属文件流
如果需要保留每个统计函数独立读取的逻辑,可以把待统计的文件名字符串作为参数传给每个统计函数,函数内部自行调用fopen打开文件,读完后立刻fclose。这样每个函数操作的都是刚打开、位置在文件头部的独立流,不会出现互相干扰的问题。
代码中存在的其他附带问题
- 原
non_blank_lines函数的空行判断逻辑不准确:fgets读取的每一行结尾都会附带换行符\n,如果某一行只有空格、制表符,读到的字符串是" \n"、"\t\n",并不是单独的"\t"或者"\n",原代码的strcmp判断无法识别这类行,会把仅含空白字符的行误判为非空行。 - 原代码的
main函数没有显式声明返回值类型,标准C规范下应写为int main(int argc, char **argv)。
内容的提问来源于stack exchange,提问作者Martin Zanony
相关产品推荐
相关产品推荐

