C语言读取900万条大数据文件时程序崩溃该如何解决?
C语言大文件读取崩溃问题修复方案
核心崩溃原因
- 栈溢出
你在栈上定义了三个大小为9000000的double数组:double tw[NUM_DATA], ip[NUM_DATA], qp[NUM_DATA]。单个double占8字节,三个数组总大小约为206MB,而操作系统默认栈空间通常仅为1MB~8MB,完全无法容纳这么大的数组,数据量小的时候不会触发栈上限,一旦调到900万就会直接崩溃。 - 野指针写入
你只给signal[num]分配了内存,但是svid[num]没有分配内存就直接执行strcpy(svid[num], h),属于野指针非法写入内存,会直接触发段错误。 - 内存分配参数错误
- 二级指针
signal、svid分配内存时多乘了MAX_LENGTH,额外浪费了9倍的指针存储空间,加剧了内存占用压力。 - 单条字符串分配时错误使用
sizeof(char *),应该用sizeof(char),同时需要预留字符串结束符\0的位置。
具体修复步骤
- 将栈上的三个大数组改为堆上动态分配,避免栈溢出
- 修正
signal、svid的内存分配逻辑,给svid的每一个元素也分配对应的字符串空间 - 修正循环判断逻辑,废弃错误的
feof用法,用fscanf的返回值作为循环终止条件 - 修正
fscanf的格式符错误,跳过不需要的字段避免内存写入错误 - 增加所有系统调用的错误判断,避免空指针访问
- 增加边界判断,避免读取数据量超过
NUM_DATA导致数组越界
修正后代码
#include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #define MAX_LENGTH 10 #define NUM_DATA 9000000 int main(int argc, char **argv){ if (argc < 2) { printf("请传入文件路径参数\n"); return 1; } // 二级指针仅分配NUM_DATA个char*的空间即可 char **signal = malloc(NUM_DATA * sizeof(char *)); char **svid = malloc(NUM_DATA * sizeof(char *)); // 大数组改到堆上分配 double *tw = malloc(NUM_DATA * sizeof(double)); double *ip = malloc(NUM_DATA * sizeof(double)); double *qp = malloc(NUM_DATA * sizeof(double)); // 检查内存分配是否成功 if (!signal || !svid || !tw || !ip || !qp) { printf("内存分配失败\n"); return 1; } char GetString[110]; int num; char a[20], b[20], c[20], d[20], e[20], f[20], g[20], h[20], z[20], x[20]; FILE *fIn = fopen(argv[1], "r"); if (!fIn) { printf("文件打开失败\n"); return 1; } // 读取表头 fgets(GetString, 109, fIn); fgets(GetString, 109, fIn); // 读取数据 num = 0; // 用fscanf返回值判断是否读取成功,最后一个字段用%*f直接跳过不需要存储 while (num < NUM_DATA && fscanf(fIn, "%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%[^','],%*f", a, b, c, d, e, f, g, h, z, x) == 10) { tw[num] = atof(a); ip[num] = atof(z); qp[num] = atof(x); // 字符串分配正确的大小,预留\0的位置 signal[num] = malloc(MAX_LENGTH * sizeof(char)); svid[num] = malloc(MAX_LENGTH * sizeof(char)); if (!signal[num] || !svid[num]) { printf("第%d条数据字符串内存分配失败\n", num); break; } // 用strncpy避免越界 strncpy(signal[num], f, MAX_LENGTH - 1); signal[num][MAX_LENGTH - 1] = '\0'; strncpy(svid[num], h, MAX_LENGTH - 1); svid[num][MAX_LENGTH - 1] = '\0'; num++; } fclose(fIn); // 这里补充后续业务逻辑,以及使用完后的内存释放逻辑避免内存泄漏 // ... 业务代码 ... // 内存释放 for (int i = 0; i < num; i++) { free(signal[i]); free(svid[i]); } free(signal); free(svid); free(tw); free(ip); free(qp); return 0; }
内容的提问来源于stack exchange,提问作者Chun Yen Huang
相关产品推荐
相关产品推荐

