【问题标题】:C program: [Done] exited with code=3221225477 in 2.322 seconds (Calloc / Free / Segmenation Error?)C 程序:[Done] 在 2.322 秒内以 code=3221225477 退出(Calloc / Free / Segmenation Error?)
【发布时间】: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)向下舍入而不是向上取整。它太小了一个。问题是宽度较小时,二维数组溢出并没有因为愚蠢的运气而破坏任何东西。但是更大的数组意味着更多的非法内存使用!!!

标签: c gcc mingw


【解决方案1】:

如果结果对其他人有用。我怀疑 calloc 和分配内存的后续使用存在问题。所以我尝试了两件事:

1:检查代码中的内存使用情况:

// Add Values & Write Line on new Day & Reset Accumulator
for (i=0; i < dataDepth; i++) {
    for (j=0; j < width; j++) {
            if (newDay) {
                fprintf(fp, ",%.2f", APE_Accum[j]);
                data_E_Array[(data_E_Index-1)*width+j] = APE_Accum[j];
                if ((data_E_Index-1)*width+j+1 > (width_T*dataDepthDay)) {
                    printf("Oh bugger...\n");
                    printf("width_T*dataDepthDay = %d\n", width_T*dataDepthDay);
                    printf("data_E_Index-1 = %d\n", data_E_Index-1);
                    printf("width = %d\n", width);
                    printf("dataDepthDay = %d\n", dataDepthDay);
                    printf("width_T = %d\n", width_T);
                    printf("j = %d\n\n", j);

真的是乱七八糟的代码,所以你可以理解我是如何忘记数组边界的。基本上,很明显我搞砸了我对calloc大小的计算。我可以找到这样的问题,但我认为这不是我的问题的可用答案,因为它会扩展到更大甚至更复杂的代码。

2:瓦尔格林德。遵循@dbush 的建议。我搬到了 Ubuntu,安装了 Valgrind,然后重新编译...

$ sudo apt install valgrind
$ ps aux | grep-i apt
$ gcc -o graphomatic ./graphomatic.c -lm -g
$ valgrind --leak-check=full --show-leak-kinds=all --verbose --track-origins=yes --log-file=valgrind-log
$ less valgrind-log

鲍勃是你的叔叔。问题一下子就出来了。我需要添加 -lm 以链接到数学库。并使用 -g 确保行号包含在 Valgrind 输出中。

==15878== Invalid write of size 4
==15878==    at 0x4038EA: _genExposureE (graphomatic.c:867)
==15878==    by 0x404A0C: genExposureE (graphomatic.c:1235)
==15878==    by 0x400EAA: main (graphomatic.c:122)
==15878==  Address 0x75cd604 is 0 bytes after a block of size 660 alloc'd
==15878==    at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==15878==    by 0x404911: genExposureE (graphomatic.c:1222)
==15878==    by 0x400EAA: main (graphomatic.c:122)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-09
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    相关资源
    最近更新 更多