проблема выделения памяти при чтении файла - PullRequest
0 голосов
/ 10 июля 2019

Я пытаюсь умножить две матрицы, хранящиеся в файле, отформатированном таким образом:

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 ]

1 голос
/ 10 июля 2019

В вашем буфере чтения есть место только для maxc (т. Е. 4) символов:

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

Затем вы пытаетесь получить строку из файла в этот буфер:

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

Но этот буфер достаточно велик только для 2 символов, за которым следует новая строка, а затем терминатор '\0'.

Глядя на ваш файл примера, самая длинная строка содержит 4 символа: "-4 1". Таким образом, в вашем буфере должно быть как минимум 6 символов (включая символ новой строки и символ '\0').

Вероятно, лучше сделать буфер немного больше.

0 голосов
/ 10 июля 2019

Проблема полностью в чтении массивов.

В 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, который передаете fgets.Если вы сделали char buf[2*maxc];, вы также должны fgets (buf, 2 * maxc, fp), что "исправит" текущую проблему.Я предпочел бы переписать все это или лучше написать fgets(buf, sizeof(buf)/sizeof(buf[0]), fp), чтобы учесть будущие изменения.

Не использовать VLA отл.char buf[maxc];.Для простоты вы можете использовать произвольный длинный буфер для строки, напр.#define LINE_MAX 1024 и char buf[LINE_MAX], а затем fgets(buf, sizeof(buf)/sizeof(buf[0]), file).Или используйте или переписайте функцию, которая будет динамически изменять размер памяти и читать строку, как GNU getline .

...