【问题标题】:memory allocation problem while reading a file读取文件时的内存分配问题
【发布时间】:2019-07-10 09:49:50
【问题描述】:

我正在尝试将存储在一个文件中的两个矩阵相乘:

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] 未被读取,因此有足够的内存来存储所有数据。

【问题讨论】:

  • 如果您只是在调试器中逐行执行代码,这应该很容易弄清楚。并且知道fgets 是如何工作的。

标签: c malloc out-of-memory


【解决方案1】:

您的读取缓冲区只有maxc(即4)字符的空间:

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

然后您尝试从文件中获取一行到该缓冲区:

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

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

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

让你的缓冲区大一点可能会更好。

【讨论】:

  • 实际上缓冲区足够大,可以容纳三个字符加上终止符,在下一次迭代中将换行符作为空行读取。
  • @Someprogrammerdude :当然,但这似乎不是这里想要的行为 - 代码在第二次迭代中无法很好地处理读取空行。
  • 感谢您也指出了这个问题,但肯定还有另一个问题,因为即使 argv[2]=3 少于 4 个字符,程序已经运行良好
  • @MichaelangeloMeucci :如果argv[2]3,那么maxc9。我假设您文件中的所有行都没有超过 7 个字符?
  • @MichaelangeloMeucci :是时候听从 Someprogrammerdude 的建议了,拿出调试器。
【解决方案2】:

问题完全在于读取数组。

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。

您在评论中发布的修复无效,因为您只增加了缓冲区大小,但没有增加您传递给fgetssize 参数。如果你做了char buf[2*maxc];,你也应该fgets (buf, 2 * maxc, fp),这将“修复”当前的问题。我宁愿重写整个事情,或者写fgets(buf, sizeof(buf)/sizeof(buf[0]), fp) 以适应未来的变化。

不要使用VLAs ex。 char buf[maxc];。为简单起见,您可以为该行使用任意长缓冲区,例如。 #define LINE_MAX 1024char buf[LINE_MAX] 然后是 fgets(buf, sizeof(buf)/sizeof(buf[0]), file)。或者使用或重写将动态调整内存大小和读取行的函数,如GNUs getline

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-25
    • 2012-07-17
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 2011-01-07
    • 2011-03-18
    相关资源
    最近更新 更多