sscanf导致valid_lines计数器无法正常递增的问题排查
C语言文件解析函数计数异常问题排查
我编写了一个C语言文件解析函数read_file,用于读取并解析文件内容。每次valid_lines计数器递增后,printf输出显示计数正常,但最终valid_lines的值始终为1,排查后定位问题出在sscanf代码块中。
函数代码
void read_file(char *filename, int *city_num_lines, double *city_min, double *city_max, double *city_avg) { // files are in data_files/ directory char path[LINE_MAX_LENGTH] = "data_files/"; strcat(path, filename); FILE *file = fopen(path, "r"); // check if file exists if (file == NULL) { printf("Error: Could not open file %s\n", filename); return; } char buffer[LINE_MAX_LENGTH]; int valid_lines = 0; double min = BIG_NUM; double max = -BIG_NUM; double sum = 0; double avg = 0; // read line by line while (fgets(buffer, LINE_MAX_LENGTH, file) != NULL) { // skip first line if (buffer[0] == 'm') { continue; } // separate by tab (\t) double cur_max, cur_min; char delim; if (sscanf(buffer, "%lf%[\t]%lf", &cur_max, &delim, &cur_min) != 3) { // printf("skipped line %s", buffer); continue; } // increment line count valid_lines++; printf("num_lines incremented\n"); // update min and max if (cur_max > max) { max = cur_max; printf("max: %lf\n", max); } if (cur_min < min) { min = cur_min; printf("min: %lf\n", min); } sum += (cur_max + cur_min) / 2; // printf("%lf - %lf\n", min, max); } printf("num_lines: %d\n", valid_lines); // calculate average avg = sum / valid_lines; // update city values *city_num_lines = valid_lines; *city_min = min; *city_max = max; *city_avg = avg; fclose(file); // close file }
控制台输出(最后几行)
... num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines incremented num_lines: 1
问题根源与修复
问题原因
问题出在sscanf的格式字符串"%lf%[\t]%lf":
%[\t]格式说明符会匹配所有制表符,并将匹配到的内容(包括末尾的NUL终止符)写入目标地址。- 但此处仅为
delim分配了1字节的char空间,无法容纳至少2字节(制表符+NUL终止符)的内容,导致内存越界,覆盖了相邻的valid_lines变量的内存区域,使得最终计数异常。
修复方案
方案1:直接跳过分隔符(推荐)
不需要单独存储制表符,直接在格式字符串中跳过即可,同时删除无用的delim变量:
// 修改sscanf调用,直接匹配制表符并跳过 if (sscanf(buffer, "%lf\t%lf", &cur_max, &cur_min) != 2) { continue; }
方案2:限制匹配长度(若需验证分隔符)
如果需要确认分隔符是制表符,可以限制%[\t]的匹配长度,并给delim分配足够空间:
char delim[2]; // 分配足够空间存储制表符+NUL终止符 if (sscanf(buffer, "%lf%1[\t]%lf", &cur_max, delim, &cur_min) != 3) { continue; }
内容的提问来源于stack exchange,提问作者h4ppyturt1e
相关产品推荐
相关产品推荐

