【发布时间】:2020-12-17 20:44:23
【问题描述】:
我正在编写一个 C 程序来处理大量 CSV 数据。它在开发中运行良好,测试文件很小。但是当文件的大小增加时,它开始失败。根据我是用 gcc 还是 minGW 的 gcc 编译它,它会在不同的地方出现分段错误或 3221225477 / 0xC0000005 失败,总是:
if (fclose(fp)) {
printf("Error closing file: %s, %s, %d.\n", fileName, __func__, __LINE__);
exit(300);
}
注意它不会通过 fclose()。或其中之一:
data_PE_T12 = calloc(width_T*dataDepthDay, sizeof(*data_PE_T12));
很长,所以我会尝试显示相关部分。首先是Main函数:
#include <stdio.h>
#include <string.h> // strtok
#include <stdlib.h> // atoi & atof
#include <time.h> // time functions
#include <math.h> // expf()
...
// Array Sizes
static int dataDepth, dataDepthDay;
static int fromTime, toTime;
static int width, width_T, width_H, width_C;
// Array Pointers
static int *timeArray, *timeArrayDay, *timeArrayPE;
static struct sensorHeader_t *headerArray, *headerArray_T, *headerArray_H, *headerArray_C;
// of depth dataDepthDay
static float *data_E_T25, *data_E_T30;
static float *data_E_T12, *data_E_T18, *data_E_H60, *data_E_H70, *data_E_C1500;
static float *data_PE_T12, *data_PE_T18, *data_PE_H60, *data_PE_H70, *data_PE_C1500;
... plus loads more.
// functions
void grabDepth(void); // OK
void grabPayload(void); // OK
... plus loads more.
int main(int argc, char **argv)
{
// Grab Input File Name
if (argc == 2) {
strcpy(rawFile, "in/");
strcat(rawFile, argv[1]);
} else { // dev
strcpy(rawFile, "in/sensor_report.csv");
}
printf("size max = %d", __SIZE_MAX__);
// Parse and Copy File
grabDepth();
grabPayload();
// Run functions
genRawData(); // Raw T, H & C files
genExposureE(); //
genExposureAPE(); //
return 0;
}
接下来是调用的第一个函数。这会打开主输入文件并提取一些数组宽度和深度,这些宽度和深度用于对已声明为静态指针的数组进行 calloc。这个想法是,随着文件大小的增加,这将使内存处理变得更好和灵活......
void grabDepth(void)
{
// 1. Open File
FILE *fp = fopen(rawFile, "r");
char buf[15000]; // Big enough to deal with lots of devices.
if (!fp) {
printf("Can't open the file: %s: %s, %d.\n", rawFile, __func__, __LINE__);
exit(100);
}
while (fgets (buf, sizeof(buf), fp)) {
int lineLen = strlen(buf);
int colNum = 1;
char *field = strtok(buf, ",");
if (field && strcmp(field, "From") == 0) {
// printf("\n\n*** row 2 ***\n\n");
// int fromTime, toTime = 0;
while (field) {
if (colNum == 2) {
fromTime = atof(field);
}
if (colNum == 4) {
toTime = atof(field);
}
field = strtok(NULL, ",");
colNum++;
}
// printf("FromTime = %d. ToTime = %d.\n", fromTime, toTime);
dataDepth = ( toTime - fromTime )/900;
// printf("dataDepth = %d.\n", dataDepth);
continue; // to next iteration.
}
// 3. Grab file width from line 10 (commsType) Check if buf Overruns too
if (field && strcmp(field, "TimeStamp") == 0) {
// First Check Line is long enough!
if (lineLen == sizeof(buf)-1) { // buf has overrun!
printf("File Read-Line Overrun: %s, %d.\n", rawFile, __func__, __LINE__);
exit(200);
}
// printf("Line Length = %d\n", lineLen);
// printf("Buf Size = %d\n", sizeof(buf));
width = -2; // ignore timestamps : I ballsed up the commas in csv file (-2 instead of -1)
while (field) {
if(field = strtok(NULL, ",")) {
width ++;
}
}
break; // out of loop!
}
}
//dataDepthDay = dataDepth/96 + (dataDepth % 96 !=0); // round up?!
dataDepthDay = dataDepth/96; // round down?!
printf("\n 1. grabDepth() Results\n");
printf( "------------------------\n");
printf("Raw Data Width = %d\n", width);
printf("Raw Data Depth = %d\n", dataDepth);
printf("dataDepthDay Depth = %d\n\n", dataDepthDay);
if (fclose(fp)) {
printf("Error closing file: %s, %d.\n", rawFile, __func__, __LINE__);
exit(300);
}
}
之后就是一个接一个地调用函数,所有这些都遵循以下一般模式:
void _genRawData(char* sensorType, struct sensorHeader_t *sensorHeader, float *dataArray, int *timeArray, size_t dataDepth, size_t width) {
FILE *fp;
strcpy(fileName, "out/");
strcat(fileName, sensorType);
strcat(fileName, "_raw.csv");
fp = fopen(fileName, "w");
// check file opened OK.
if (fp == NULL) {
printf("Error! Couldn't Create file: %s\n", fileName);
return;
}
printf("building file : %s\n", fileName);
// Allocate Memory
timeArrayDay = calloc(dataDepthDay, sizeof(*timeArrayDay));
timeArrayPE = calloc(dataDepthDay, sizeof(*timeArrayPE)); // xxxx same array as day time array!?
data_E_T12 = calloc(width_T*dataDepthDay, sizeof(*data_E_T12));
data_E_T18 = calloc(width_T*dataDepthDay, sizeof(*data_E_T18));
data_E_H60 = calloc(width_H*dataDepthDay, sizeof(*data_E_H60));
data_E_H70 = calloc(width_H*dataDepthDay, sizeof(*data_E_H70));
// do stuff and build new arrays up and put into files...
if (fclose(fp)) {
printf("Error closing file: %s, %d.\n", rawFile, __func__, __LINE__);
exit(300);
}
}
我只在每个二维数组上调用了 calloc 一次,为了调试,我删除了 free() 调用。
我认为我在内存管理方面做错了,当数组大小超过某个点时,这让我很痛苦,但我不知道出了什么问题。我试图确保我访问的内存已正确分配并在一台功能强大的实际计算机上工作(我通常是嵌入式人员),我不希望操作系统分发数据有任何问题?是不是有很多事情要做!?
【问题讨论】:
-
通过 valgrind 运行您的代码。如果你对内存管理不善,它会告诉你在哪里。
-
??????小心前行,看看会发生什么?
-
如果
fclose失败,我猜fp被缓冲区溢出损坏了。尝试三件事:1) 在fopen之后打印fp的值,并在fclose之前再次打印,2) 将buffer声明为static buffer[50000],或3) 在函数外部声明buffer,即把它变成一个全局变量,并把它变大。 -
rawFile是如何定义的,您确定它的大小足以容纳您在其中存储的内容吗? -
@user3386109 和上面的其他 cmets!你让我更详细地阅读了 Valgrind 和 GDB。通过这个,我能够诊断出问题!这是对数组(calloc width*depth)和填充它的后续代码的分配不足。出于某种奇怪的原因,我输入了正确的代码,但已将其注释掉,将计算出的必要数组深度(dataDepthDay)向下舍入而不是向上取整。它太小了一个。问题是宽度较小时,二维数组溢出并没有因为愚蠢的运气而破坏任何东西。但是更大的数组意味着更多的非法内存使用!!!