读取文件时出现内存分配问题

时间:2019-07-10 09:49:50

标签: c malloc out-of-memory

我正在尝试将存储在这样格式化的文件中的两个矩阵相乘:

1 2
2 3
*
-4 1
1 0

最初我不知道每个矩阵的维数是多少。但是我让用户定义它,否则采用默认值100

int maxc = argc > 2 ? atoi(argv[2]) * atoi(argv[2]) : 100;

我已经可以正确执行计算了,但是我注意到,如果我输入维度argv[2] = "2",以使maxc = 8(对于本示例来说应该足够),则在读取时会产生错误。或打印文件。但是,如果我输入argv[2] = "3",则在此示例中一切正常。由于maxc用于在此处分配内存:matrix = malloc(maxc * sizeof *matrix),我怀疑问题可能在该行上。我是否也应该为size_t row; size_t col;分配内存?

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string.h>

#define MAXNOP 50 /*Max number of operations allowed */
#define MAXNMATR 20 /*Max number of matrices */

struct m {
    size_t row;
    size_t col;
    double *data;
};

struct m multiply(struct m *A, struct m *B);
void f(double x);
void print_matrix(struct m *A);
void read_file(int maxc, FILE *fp);
void scalar_product(double scalar, struct m *B);
void calculate(struct m *matrix, int nop, int id, char *op);

int main(int argc, char *argv[]) {

    FILE *file = argc > 1 ? fopen(argv[1], "rb") : stdin;

    /* define max dimension of a matrix */
    int maxc = argc > 2 ? atoi(argv[2]) * atoi(argv[2]) : 100;
    read_file(maxc, file);       

    return 0;
}

void read_file(int maxc, FILE *fp) {
    struct m *matrix;
    int id = 0; /* id of a matrix */
    size_t ncol, nrow; /* No of columns of a matrix*/
    ncol = nrow = 0;
    int nop = 0; /*No of operators*/
    int off = 0;
    int i;
    int n;
    double *d;
    char buf[2 * maxc]; /*to store each lines of file */
    char *p = buf;
    char op[MAXNOP];

    for (i = 0; i < MAXNOP; i++)
        op[i] = '?';    

    if (!(matrix = malloc(maxc * sizeof *matrix))) {
        perror("malloc-matrix");
        exit(1);
    }

    /* Read file line by line */
    while (fgets(buf, maxc, fp)) {
        if (nrow == 0) {
            /* allocate/validate max no. of matrix */
            d = matrix[id].data = malloc(sizeof(double) * MAXNMATR);
        }     
        /* check if line contains operator */
        if ((!isdigit(*buf) && buf[1] =='\n')) {      
            op[nop++] = *buf;
            matrix[id].col = ncol;
            matrix[id].row = nrow;
            nrow = ncol = 0;
            id++;
            continue;
        } else {
            /* read integers in a line into d */
            while (sscanf(p + off, "%lf%n", d, &n) == 1) {
                d++;
                if (nrow == 0)
                    ncol++;
                off += n;
            }
            nrow++;
            off = 0;
        }
    } /*end of while fgets cycle */

    /* Assign last matrix No of columns and rows */
    matrix[id].col = ncol;
    matrix[id].row = nrow;

    /* Printing the matrices and operations */
    for (i = 0; i <= id; i++) {  
        if (op[i] == '*' || op[i] == '-' || op[i] =='+') {
            print_matrix(&matrix[i]);
            if (op[i-1] != 'i')
                printf("%c\n", op[i]);
            else
                continue;
        } else      
        if (op[i] == '?') {
             print_matrix(&matrix[i]);
        }        
    }

    calculate(matrix, nop, id, op);
}

void calculate(struct m *matrix, int nop, int id, char *op) {    
    int i;

    for (i = 0; i <= nop; i += 2) {        
        if (op[i] == '*' && op[i+1] == '?') {
             if (matrix[i].row == 1 && matrix[i].col == 1)
                 scalar_product(matrix[i].data[0], &matrix[i + 1]); //Multiplication of Scalar per matrix
             else {                 
                 matrix[i + 1] = multiply(&matrix[i], &matrix[i + 1]);
                 matrix[i + 2] = multiply(&matrix[i + 1], &matrix[i + 2]);
             }
             break;
         }       
    }

    printf("=\n");
    print_matrix(&matrix[id]); /* Print the result */
    free(matrix);
}

struct m multiply(struct m *A, struct m *B) { 
    size_t i, j, k;
    struct m C;
    C.data = malloc(sizeof(double) * A->row * B->col);

    C.row = A->row;
    C.col = B->col;

    for (i = 0; i < C.row; i++)
        for (j= 0 ; j < C.col; j++)
            C.data[i * C.col + j] = 0;

    // Multiplying matrix A and B and storing in C.
    for (i = 0; i < A->row; ++i)
        for (j = 0; j < B->col; ++j)
            for (k = 0; k < A->col; ++k)
              C.data[i * C.col + j] += A->data[i * A->col + k] * B->data[k * B->col + j];

    return C;
}

void f(double x) {
    double i, f = modf(x, &i);

    if (f < .00001)
        printf("%.f ", i);
    else
        printf("%f ", x);
}

/* printing a Matrix */

void print_matrix(struct m *A) {
     size_t i, j;
     double *tmp = A->data;

     for (i = 0; i < A->row; i++) {
        for (j = 0; j < A->col; j++) {
            f(*(tmp++));
        }
        putchar('\n');
    }
}

void scalar_product(double scalar, struct m *B) {
     size_t i, j;

     for (i = 0; i < B->row; i++)
        for (j = 0; j < B->col; j++)
           B->data[i * B->col + j] = scalar * B->data[i * B->col + j];
}

预期结果是这样的:https://ideone.com/Z7UtiR

此处argv[2]未被读取,因此有足够的内存来存储所有数据。

2 个答案:

答案 0 :(得分:1)

您的读取缓冲区只能容纳maxc(即4)个字符:

char buf[maxc]; /*to store each lines of file */

然后,您尝试将文件中的一行插入该缓冲区:

while (fgets (buf, maxc, fp)){

但是该缓冲区仅足够容纳2个字符,然后是换行符,然后是'\0'终止符。

在示例文件中,最长的一行包含4个字符:"-4 1"。因此,您的缓冲区至少需要容纳6个(包括换行符和'\0'终止符)。

最好使缓冲区更大一些。

答案 1 :(得分:0)

问题完全在于读取数组。

maxc = 4和缓冲区char buf[maxc];只能放置3个字符和结尾字符。

所以fgets (buf, maxc, fp)

  • 第一个将读取buf = "1 2"(3个字符和零字节)
  • 第二秒钟将显示buf = "\n"(1个换行符,fgets终止)
  • 然后读取buf = "2 3"
  • 然后读取buf = "\n"
  • buf = "*\n"
  • buf = "-4 "
  • 依此类推

由于空行,此代码内的内容被剪断了:

    else /* read integers in a line into d */
    {
        while (sscanf (p + off, "%lf%n", d, &n) == 1) {
            d++;
            if(nrow == 0)
                ncol++;
            off += n;
        }
        nrow++;
        off = 0;
    }

变量nrow将增加4倍(行读取2倍,空行仅读取换行2倍),这将增加2倍。第二个矩阵将具有1列,因为您将从该行仅读取-4,因此您的while(sscanf循环将仅扫描一个数字,因此ncol将仅扫描1。

您在评论中发布的修订无效,因为您仅增加了缓冲区大小,但没有增加传递给size的{​​{1}}参数。如果您做了fgets,还应该char buf[2*maxc];,它将“解决”当前问题。我宁愿重新编写整个内容,也不愿编写fgets (buf, 2 * maxc, fp)以适应将来的更改。

请勿使用VLAsfgets(buf, sizeof(buf)/sizeof(buf[0]), fp)。为简单起见,您可以为该行使用任意长缓冲区,例如。 char buf[maxc];#define LINE_MAX 1024,然后char buf[LINE_MAX]。或者使用或重写函数来动态调整内存大小并读取行,例如GNUs getline