You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

错误根源

  1. 未初始化指针tr非法写内存:代码中int *tr;仅声明未分配内存,sscanf直接向tr指向的未知地址写入数据,导致堆内存被破坏。这种破坏是随机的,Steps值越大,内存占用越多,破坏越快触发段错误,和blocks数组本身无关。
  2. 文件读取未做边界校验:嵌套循环中调用fgets未检查返回值,若文件提前结束,dp会变为无效指针,后续fgets调用会访问非法地址。
  3. cells数组越界风险:计算row和col时未校验范围,若坐标超出预期区间,会导致cells[cl*row+col]访问越界,进一步破坏内存。

修复步骤

  1. 替换未初始化的tr指针:使用栈上变量替代指针,避免非法内存写入:
    // 替换原int *tr;声明
    int tr;
    // 修改sscanf调用,传入变量地址
    sscanf(lines, "%i %i %lf %lf", &tr, &tr, &x[n], &y[n]);
    
  2. 添加文件读取校验:所有fgets调用后检查返回值,处理文件提前结束的情况:
    for (int i=0; i<8; i++){
        if(!fgets(lines, 200, dp)){
            // 提前退出循环,避免无效操作
            goto cleanup;
        }
    }
    
    同理,遍历N个坐标的循环中也要添加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]);
    }
    
  3. 校验数组索引合法性:确保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]);
    }
    
  4. 释放分配的内存:程序结束前释放所有堆内存,避免内存泄漏:
    cleanup:
    free(blocks);
    free(cells);
    free(x);
    free(y);
    if(dp) fclose(dp);
    return 0;
    

内容的提问来源于stack exchange,提问作者Min Jae Kim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:23:22