Dynami c матричный 2D-массив mallo c () из выделения внешней функции - PullRequest
1 голос
/ 05 мая 2020

Я пытаюсь загрузить файл из неосновной функции с указателем на указатель (2D-матрица) в качестве аргумента в ANSI C.

Подход с функцией l-значения правильный:

float **loadArray(int *rows, int *columns) {
    float **array;
    FILE *instream; // input file pointer
    char infile[21] = {}; //20 chars max filename length
    int i = 0, j = 0; //iterators
    printf("filename to load data from: ");
    scanf(" %20s", infile);
    if (!(instream = fopen(infile, "r"))) {
        perror("fopen() error");
        exit(-1);
    }
    fread(rows, sizeof(int), 1, instream);
    fread(columns, sizeof(int), 1, instream);
    fprintf(stdout, "\narray(%d,%d):", *rows, *columns); //OK
    // allocation of vertical array containing rows pointers.
    if (!(array = (float**)malloc((*rows) * sizeof(float*)))) {
        printf("vertical malloc() error");
        exit(-1);
    }
    for (i = 0; i < (*rows); i++) 
        // for every row allocate columns space
        if (!(array[i] = (float*)malloc((*columns) * sizeof(float)))) {
            printf("horizontal malloc() error");
            exit(-1);
        }
    for (i = 0; i < (*rows); i++)
        for (j = 0; j < (*columns); j++)
            fread((&array[i][j]), sizeof(float), 1, instream);
    fclose(instream);
    return array;
}

int main() {
    int i = 0;
    int rows = 0, columns = 0;
    float **myarray;
    myarray = loadArray(&rows, &columns);
    ...
    for (i = 0; i < rows; i++)
        free(myarray[i]);
    free(myarray);
}

Но я пытаюсь быть последовательным при чтении из файла, и rows, columns передаются как адреса для передачи массива таким же образом:

int loadArray2(float ***array, int *rows, int *columns) {
    FILE *instream; // input file pointer
    char infile[21] = {}; //20 chars max filename length
    int i = 0, j = 0; //iterators
    printf("filename to load data from: ");
    scanf(" %20s", infile);
    if (!(instream = fopen(infile, "r"))) {
        perror("fopen() error");
        exit(-1);
    }
    fread(rows, sizeof(int), 1, instream);
    fread(columns, sizeof(int), 1, instream);
    fprintf(stdout,"\narray(%d,%d):", *rows, *columns); //OK
    // allocation of vertical array containing rows pointers.
    if (!(*array = (float**)malloc((*rows) * sizeof(float*)))) {
        printf("vertical malloc() error");
        exit(-1);
    }
    for (i = 0; i < (*rows); i++) 
        // for every row allocate columns space
        if (!(*array[i] = (float*)malloc((*columns) * sizeof(float)))) {
            printf("horizontal malloc() error");
            exit(-1);
        }
    for (i = 0; i < (*rows); i++)
        for (j = 0; j < (*columns); j++)
            fread((array[i][j]), sizeof(float), 1, instream);
    fclose(instream);
    return 0;
}

int main() {
    int rows = 0, columns = 0;
    float **myarray;
    loadArray2(&myarray, &rows, &columns);
    ...
    for (i = 0; i < rows; i++)
        free(myarray[i]);
    free(myarray);
}

Но этот подход не смогли. Думаю, я сделал ошибку, malloc() вызвав либо отсутствие ошибок, либо предупреждений, либо мой лог c плохой, я признаю, что заблудился ...

Спасибо за несколько советов.

Ответы [ 2 ]

1 голос
/ 05 мая 2020

В функции с ошибкой основная проблема заключается в том, что array является указателем на float **, указатель, который необходимо разыменовать, чтобы получить исходную переменную float **. Точно так же, как вы делаете с аргументами rows и columns.

Поэтому вместо, например, array[i][j], вам нужно сделать (*array)[i][j].

0 голосов
/ 05 мая 2020

Когда вы используете тройной указатель в loadArray2, помните, что постфиксные операторы связываются сильнее, чем префиксные операторы. Поэтому вы должны писать (*array)[i] при сохранении указателя на выделенный массив float и &(*array)[i][j] при чтении значения с плавающей запятой.

Обратите внимание, что в вашем коде есть другие проблемы:

  • char infile[21] = {}; не является допустимым синтаксисом в C (он находится на C ++ и может поддерживаться вашим компилятором в качестве расширения или, возможно, потому, что вы компилируете этот код как C ++). Вместо этого напишите char infile[21] = "";.
  • Поскольку вы читаете двоичные данные из файла, вы должны открыть его в двоичном режиме: fopen(infile, "rb"). Это может не иметь значения в linux и Mac / OS, но определенно является ошибкой в ​​устаревших системах.
  • Вы должны проверить наличие ошибок чтения.
  • Вы можете прочитать строки матрицы за один вызов (под кодом для первого варианта):

    for (i = 0; i < (*rows); i++) {
        if (fread(array[i], sizeof(float), *cols, instream) != *cols) {
            // handle the read error
        }
    }
    

Вопрос о том, использовать ли первый или второй подход, является спорным:

  • в первом случае легко вернуть ошибку с помощью указателя NULL, поэтому передача косвенного указателя 2D-массива по ссылке кажется ненужной. Это более идиоматический c способ вернуть этот указатель в C. Если требуется дополнительная информация об ошибке, вы можете взять указатель на код ошибки в качестве дополнительного аргумента.
  • во втором случае код ошибки может быть возвращен напрямую для более точной диагностики, но тройной указатель подвержен ошибкам, как вы только что испытали. Такие типы не приветствуются. Не будьте «древовидным» программистом :)

Альтернативный подход - инкапсулировать указатель, атрибуты строк и столбцов в структуру и передать указатель на целевую структуру.

Вот модифицированная версия с таким подходом:

#include <stdio.h>
#include <stdlib.h>

struct matrix {
    int cols, rows;
    float **data;
};

void matrix_reset(struct matrix *mat) {
    int i;
    if (mat->data) {
        for (i = 0; i < mat->rows; i++)
            free(mat->data[i]);
        free(mat->data);
        mat->data = NULL;
    }
    mat->rows = 0;
    mat->cols = 0;
}

int matrix_load(struct matrix *mat) {
    FILE *instream; // input file pointer
    char infile[257] = ""; //256 chars max filename length

    printf("filename to load data from: ");
    if (scanf(" %256s", infile) != 1) {
        perror("invalid input");
        return -1;
    }
    if (!(instream = fopen(infile, "rb"))) {
        perror("fopen() error");
        return -2;
    }
    if (fread(&mat->rows, sizeof(int), 1, instream) != 1
    ||  fread(&mat->cols, sizeof(int), 1, instream) != 1) {
        perror("fread() error");
        fclose(instream);
        return -3;
    }
    fprintf(stdout, "\narray(%d,%d):", mat->rows, mat->cols);
    // allocation of vertical array containing rows pointers.
    if (!(mat->data = calloc(mat->rows, sizeof(*mat->data)))) {
        printf("vertical malloc() error");
        fclose(instream);
        return -4;
    }
    for (int i = 0; i < mat->rows; i++) {
        // for every row allocate columns space
        if (!(mat->data[i] = calloc(mat->cols, sizeof(*mat->data[i])))) {
            printf("horizontal malloc() error");
            matrix_reset(mat);
            fclose(instream);
            return -5;
        }
    }
    for (i = 0; i < mat->rows; i++)
        if (fread(&mat->data[i], sizeof(*mat->data[i]), mat->cols, instream) != mat->cols) {
            printf("horizontal malloc() error");
            matrix_reset(mat);
            fclose(instream);
            return -6;
        }
    }
    fclose(instream);
    return 0;
}

int main() {
    struct matrix m = { 0, 0, NULL};

    if (!matrix_load(&m)) {
       ...
    }
    matrix_reset(&m);
    return 0;
}
...