C++读取大文件时fgets触发Segmentation Fault的问题排查
大文件读取触发Segmentation Fault问题排查与修复
问题背景
处理9GB坐标转储文件out.dump时,使用fgets逐行读取持续触发段错误。程序将行内容存入lines数组,经sscanf解析后存入堆上分配的cells数组。
- 堆上
int类型数组blocks在Steps设为100000时触发错误,设为10000时可正常运行,内存分配未失败 - gdb与valgrind均指向
fgets调用行,valgrind错误提示:
17599 1 errors in context 2 of 10:
17599 Invalid read of size 8
17599 at 0x5245B06: fgets (iofgets.c:47)
17599 by 0x108AC0: main (play.cpp:45)
17599 Address 0x9 is not stack'd, malloc'd or (recently) free'd
相关代码:
#include <string> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <cmath> #define Side 10 #define N 40 #define D 32 #define Steps 100000 int main(int argc, char** argv) { double cell_inv=double (D)/Side; int cl = 2*D; int *blocks = (int*)malloc((cl*cl-2*cl+1)*Steps*sizeof(int)); if (blocks==NULL){ printf("mem alloc failed\n"); } printf("%i\n", blocks[0]); //all of these print out fine blocks[0]=1; //prints fine printf("%i\n", blocks[0]); //prints fine int *cells = (int*)malloc(cl*cl * sizeof(int)); FILE *dp; dp = fopen("./out.dump", "r"); char lines[200]; int *tr; double *x, *y; x = (double*)malloc(N * sizeof(double)); y = (double*)malloc(N * sizeof(double)); long int t=0;//keeps track of how much I am looping through int row,col; while(fgets(lines, 200, dp)!=NULL){ //segfault here when t=241 on terminal, t=2374 valgrind for (int i=0; i<cl; i++){ for (int j=0; j<cl; j++){ cells[cl*i+j]=0; } } for (int i=0; i<8; i++){ fgets(lines, 200, dp); } //ignoring first 8+1 lines of dump file for (int n=0; n < N; n++){ fgets(lines, 200, dp); printf("%s\n", lines); sscanf(lines, "%i %i %lf %lf", tr, tr, &x[n], &y[n]); } for (int n=0; n < N; n++){ row=int ((y[n]+Side)*cell_inv); col=int ((x[n]+Side)*cell_inv); cells[cl*row+col]=1; } printf("%li\n", t); t++; } return 0; }
错误根源
- 未初始化指针
tr非法写内存:代码中int *tr;仅声明未分配内存,sscanf直接向tr指向的未知地址写入数据,导致堆内存被破坏。这种破坏是随机的,Steps值越大,内存占用越多,破坏越快触发段错误,和blocks数组本身无关。 - 文件读取未做边界校验:嵌套循环中调用
fgets未检查返回值,若文件提前结束,dp会变为无效指针,后续fgets调用会访问非法地址。 cells数组越界风险:计算row和col时未校验范围,若坐标超出预期区间,会导致cells[cl*row+col]访问越界,进一步破坏内存。
修复步骤
- 替换未初始化的
tr指针:使用栈上变量替代指针,避免非法内存写入:// 替换原int *tr;声明 int tr; // 修改sscanf调用,传入变量地址 sscanf(lines, "%i %i %lf %lf", &tr, &tr, &x[n], &y[n]); - 添加文件读取校验:所有
fgets调用后检查返回值,处理文件提前结束的情况:
同理,遍历N个坐标的循环中也要添加for (int i=0; i<8; i++){ if(!fgets(lines, 200, dp)){ // 提前退出循环,避免无效操作 goto cleanup; } }fgets校验:for (int n=0; n < N; n++){ if(!fgets(lines, 200, dp)){ fprintf(stderr, "Unexpected end of file at step %ld\n", t); goto cleanup; } printf("%s\n", lines); sscanf(lines, "%i %i %lf %lf", &tr, &tr, &x[n], &y[n]); } - 校验数组索引合法性:确保
row和col在cells数组的有效范围内:row = int((y[n]+Side)*cell_inv); col = int((x[n]+Side)*cell_inv); if(row >= 0 && row < cl && col >= 0 && col < cl){ cells[cl*row+col] = 1; } else { fprintf(stderr, "Invalid coordinate at step %ld, n=%d: x=%.2lf, y=%.2lf\n", t, n, x[n], y[n]); } - 释放分配的内存:程序结束前释放所有堆内存,避免内存泄漏:
cleanup: free(blocks); free(cells); free(x); free(y); if(dp) fclose(dp); return 0;
内容的提问来源于stack exchange,提问作者Min Jae Kim
相关产品推荐
相关产品推荐

