Матрица (двухмерный массив) и ее значения неправильно записываются в текстовый файл

В моей программе я позволяю пользователю определять размеры своей матрицы, а затем вводить значение каждой ячейки в матрице. Затем эта матрица сохраняется в файле .txt в виде двоичных данных. Иногда с некоторыми матрицами определенных значений и размеров матрица не считывается должным образом с правильными значениями в правильных ячейках.

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

int numberOfMatrices = 0;
int orderedMatricesInfo[100][1][2];
int matrixRows = 0;
int matrixCols = 0;

int** initMatrix(int rows, int cols);
void saveMatrix(int** matrix, int rows, int cols);
void* allocateMatrix(int rows, int cols);
int** readMatrix(int rows, int cols, int matrix[matrixRows][matrixCols], int matrixNum);
void displayMatrices();
void displayMatrix(int rows, int cols, int** matrix);

int main()
{

    int n, m;
    int** matrixPointer;

    printf("Please enter the number of rows in your matrix: \n");
    scanf("%d", &n);

    printf("Please enter the number of columns in your matrix: \n");
    scanf("%d", &m);

    matrixPointer = initMatrix(n, m);

    printf("%d\n", matrixPointer[0][0]);

    displayMatrices();

    printf("SUCCESS");
}

int** initMatrix(int rows, int cols)
{
    int matrix[rows][cols];
    int **matrixPointer;

    matrixPointer = malloc(sizeof(int*)*cols);

    for (int i =0; i < cols; ++i)
    {
        matrixPointer[i] = malloc(sizeof(int*)*cols);
    }

    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            printf("\nPlease enter element for [%d][%d]: \n", i, j);
            scanf("%d", &matrixPointer[i][j]);
        }
    } 

    ++numberOfMatrices;

    orderedMatricesInfo[numberOfMatrices-1][0][0] = rows;
    orderedMatricesInfo[numberOfMatrices-1][0][1] = cols;

    allocateMatrix(rows, cols);

    saveMatrix(matrixPointer, rows, cols);
    return matrixPointer;
}

void saveMatrix(int** matrix, int rows, int cols)
{   
    char fullFileName[100] = "matrix";
    char fileNameExtension[100] = ".txt";
    char strNum[100];

    sprintf(strNum, "%d", numberOfMatrices);

    strcat(strNum, fileNameExtension);
    strcat(fullFileName, strNum);

    FILE *file = fopen(fullFileName, "ab");

    for(int i=0; i<rows; ++i)
    {
        fwrite(matrix[i], sizeof(int), rows, file);
    }
    fclose(file);
}

void* allocateMatrix(int rows, int cols)
{
    return malloc(sizeof(int[rows][cols]));
}

int** readMatrix(int rows, int cols, int matrix[matrixRows][matrixCols], int matrixNum) 
{
    int **matrixPointer;
    matrixPointer = malloc(sizeof(int*)*cols);

    for (int i =0; i < cols; ++i)
    {
        matrixPointer[i] = malloc(sizeof(int*)*cols);
    }

    char fullFileName[100] = "matrix";
    char fileNameExtension[100] = ".txt";
    char strNum[100];

    sprintf(strNum, "%d", matrixNum);

    strcat(strNum, fileNameExtension);
    strcat(fullFileName, strNum);

    FILE *file;
    file=fopen(fullFileName, "rb");
    fread(matrix, sizeof(int[rows][cols]), 1, file); // read 1 2D-array

    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            matrixPointer[i][j] = matrix[i][j];
            printf("%d", matrixPointer[i][j]);
        }
    }

    allocateMatrix(rows, cols);

    return matrixPointer;
}

void displayMatrices()
{
    for (int matrixNum = 1; matrixNum <= numberOfMatrices; ++matrixNum)
    {
        char fullFileName[100] = "matrix";
        char fileNameExtension[100] = ".txt";
    
        char strNum[100];
        sprintf(strNum, "%d", matrixNum);
        strcat(strNum, fileNameExtension);
        strcat(fullFileName, strNum);

        matrixRows = orderedMatricesInfo[matrixNum -1][0][0];
        matrixCols = orderedMatricesInfo[matrixNum -1][0][1];

        int matrix[matrixRows][matrixCols];
        int** matrixPointer;

        matrixPointer = readMatrix(matrixRows, matrixCols, matrix, matrixNum);

        printf("matrix %d", matrixNum);
        displayMatrix(matrixRows, matrixCols, matrixPointer);
    }
}

void displayMatrix(int rows, int cols, int** matrix)
{
    for (int i = 0; i < rows; ++i)
    {
        printf("\n");
        for (int j = 0; j < cols; ++j)
        {
            printf("%d ", matrix[i][j]);
        }
    }
    printf("\n");
}

В основной функции, когда пользователь создает матрицу, матрица сохраняется во вновь созданном файле с именем matrix1.txt как двоичные данные. Однако я думаю, что он не хранится должным образом, так как при обратном чтении матрица отображается неправильно с правильными значениями. Например, если я создаю матрицу:

[12, 19, 17, 15,
120, 566, 214, 153,
124, 1, 2, 3]

А затем попытайтесь отобразить матрицу, тогда матрица выше не отображается, вместо этого отображается это:

[12, 19, 17, 120,
566, 214, 124, 1,
2, 0, 101792956, 1]

Я неправильно распределил память в этой программе? В чем проблема? Поскольку ему удается получить и правильно отобразить первые 3 элемента верхней строки. Я новичок в C, поэтому не могу точно определить проблему. Когда матрица меньше, как матрица 2x2, тогда все работает нормально...

Благодарю вас!


person Siddharth Chaudhary    schedule 11.11.2020    source источник
comment
matrixPointer = malloc(sizeof(int*)*cols); Вам нужно rows, а не cols количество указателей. Вам также нужно int не int* в matrixPointer[i] = malloc(sizeof(int*)*cols); (для cols количества int не cols количества указателей)   -  person David C. Rankin    schedule 11.11.2020
comment
Я применил изменения, однако теперь я получаю ошибку сегментации, если пытаюсь создать матрицу 3x4?   -  person Siddharth Chaudhary    schedule 11.11.2020
comment
Мне понадобится минута, чтобы пройтись по остальным, это то, что сразу же бросилось в глаза. orderedMatricesInfo[numberOfMatrices-1][0][0] = rows; выглядит довольно подозрительно. Предоставьте минимальный, полный и проверяемый пример (MCVE). (что-то, что я могу скопировать и скомпилировать без создания прототипов функций и добавления заголовков и т. д.)   -  person David C. Rankin    schedule 11.11.2020
comment
ммм как получилось? Я должен был отметить, что этот трехмерный массив отвечает за хранение количества строк и столбцов каждого массива. Когда матрица создана, переменная numberOfMatrices увеличивается, а затем количество строк сохраняется в [1][0][0], а количество столбцов матрицы сохраняется в [1][0][1]. .   -  person Siddharth Chaudhary    schedule 11.11.2020
comment
Вот почему я говорю, дайте мне минуту, чтобы пройти оставшуюся часть кода. Вы должны изменить свой цикл после перехода на rows, вы должны выполнить цикл for (int i =0; i < rows; ++i)   -  person David C. Rankin    schedule 11.11.2020
comment
Это внутри initMatrix?   -  person Siddharth Chaudhary    schedule 11.11.2020


Ответы (1)


В вашем коде по-прежнему отсутствует информация, но ваша функция initMatrix() имеет большое количество мелких проблем. Вы используете cols, где rows следует использовать для выделения указателей. Вы не можете проверить какое-либо распределение или пользовательский ввод. Это рецепт неопределенного поведения. Кроме того, вы не предоставляете код для определения того, что такое numberOfMatrices или как объявляется orderedMatricesInfo. С учетом этих предостережений вы можете исправить немедленные ошибки в initMatrix() следующим образом:

int **initMatrix(int rows, int cols)
{
    int matrix[rows][cols];
    int **matrixPointer;

    /* allocate rows number of pointers */
    matrixPointer = malloc (sizeof *matrixPointer * rows);
    if (!matrixPointer) {                           /* validate EVERY allocation */
        perror ("malloc-*matrixPointer");
        return NULL;
    }

    /* you must allocate rows number of cols * sizeof(int) blocks
     * assigning the starting address to each of your allocated pointers.
     * (always use the dereferenced pointer to set type-size)
     */
    for (int i = 0; i < rows; ++i)
    {   /* use calloc on rows to initialize all bytes zero */
        matrixPointer[i] = calloc (sizeof *matrixPointer[i], cols);
        if (!matrixPointer[i]) {                    /* validate EVERY allocation */
            perror ("calloc-matrixPointer[i]");
            while (i--)                             /* on error, free prior memory */
                free (matrixPointer[i]);
            free (matrixPointer);
            return NULL;
        }
    }

    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            while (1) { /* loop continually until valid integer provided */
                printf ("\nPlease enter element for [%d][%d]: \n", i, j);
                
                /* validate EVERY user-input */
                if (scanf("%d", &matrixPointer[i][j]) == 1)
                    break;
                    
                fputs ("  error: invalid integer input.\n", stderr);
            }
        }
    } 
    
    /* your code does not provide information on the following */
    ++numberOfMatrices;

    orderedMatricesInfo[numberOfMatrices-1][0][0] = rows;
    orderedMatricesInfo[numberOfMatrices-1][0][1] = cols;

    allocateMatrix(rows, cols);     /* why allocate -- that's what you just did?? */

    saveMatrix(matrixPointer, rows, cols);
    
    return matrixPointer;
}

(не проверено, так как ваш код неполный. Код прокомментирован, объясняя дополнения)

Обратите внимание, ваш allocateMatrix(int rows, int cols) просто теряет память. Возврат не назначается какому-либо указателю, поэтому вы потеряли начальный адрес выделенного блока и не можете его освободить.

В любом написанном вами коде, который динамически выделяет память, у вас есть 2 обязанности в отношении любого выделенного блока памяти: (1) всегда сохранять указатель на начальный адрес для блока памяти. память, поэтому (2) ее можно освободить, когда она больше не нужна.

Дайте мне знать, если у вас есть вопросы, и предоставьте минимальный, полный и проверяемый пример (MCVE) для получения дополнительной помощи.

Дополнительная информация об ошибках

Остальная часть вашего кода имеет ту же проблему rows и cols, когда вы пытаетесь выделить cols количество указателей (вместо rows количества указателей). Однако проблема, приведшая к вашему SEGFAULT, была связана с numberOfMatrices и файлом, который вы пытаетесь открыть, не по одному. Это было решено путем обеспечения того, чтобы ваши циклы последовательно индексировали 0 < numberOfMatrices и размещали номер файла с помощью:

    matrixPointer = readMatrix(matrixRows, matrixCols, matrix, matrixNum + 1);

Добавление 1 к matrixNum, переданному readMatrix. Это простой способ сохранить прямую нумерацию. Ваш код записывает и читает файл правильно, но обратите внимание, что вы выполняете добавление к файлу, поэтому вам придется дополнительно работать, чтобы получить доступ к каждой из матриц, хранящихся в вашем выходном файле (вы должны указать количество rows и cols в файл перед записью каждой матрицы, поэтому вы можете прочитать файл обратно, прочитав 2 значения int, чтобы определить размер следующей матрицы, которая остается вам.

Когда он находится, ваша функция отображения будет читать файл, предполагая, что первые целые числа в файле относятся к текущей матрице, что не так. Целочисленные значения, прочитанные для отображения, являются элементами первой матрицы, записанной в файл, которые не будут совпадать с текущими значениями матрицы, если только вы не записываете и не читаете первый набор значений. В противном случае текущие значения матриц содержатся в файле после байтов всех предыдущих матриц, которые были записаны в файл. Более полезным форматом файла будет:

no_matricies rows cols ...... rows cols ......

Где первое записанное целочисленное значение содержит количество матриц, записанных в файл, за которым следуют row, col и .... data для каждой матрицы, что позволит получить доступ ко всем матрицам и позволит обновить первое целочисленное значение в файле без повторного запись всего файла. (это тоже остается на ваше усмотрение)

Ваш очищенный код выглядит следующим образом. Примечание. Я изменил использование вами sprintf и устранил ненужную конкатенацию. Я также упростил копирование из matrix в matrixPointer, используя memcpy():

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

int numberOfMatrices = 0;
int orderedMatricesInfo[100][1][2];
int matrixRows = 0;
int matrixCols = 0;

int** initMatrix(int rows, int cols);
void saveMatrix(int** matrix, int rows, int cols);
void* allocateMatrix(int rows, int cols);
int** readMatrix(int rows, int cols, int matrix[matrixRows][matrixCols], int matrixNum);
void displayMatrices();
void displayMatrix(int rows, int cols, int** matrix);

int main()
{

    int n, m;
    int** matrixPointer;

    printf("Please enter the number of rows in your matrix: \n");
    if (scanf("%d", &n) != 1) {
        fputs ("error: invalid integer input.\n", stderr);
        return 1;
    }

    printf("Please enter the number of columns in your matrix: \n");
    if (scanf("%d", &m) != 1) {
        fputs ("error: invalid integer input.\n", stderr);
        return 1;
    }

    matrixPointer = initMatrix(n, m);

    // printf("%d\n", matrixPointer[0][0]);

    displayMatrices();

    printf("SUCCESS");
}

int **initMatrix(int rows, int cols)
{
    int **matrixPointer;

    /* allocate rows number of pointers */
    matrixPointer = malloc (sizeof *matrixPointer * rows);
    if (!matrixPointer) {                           /* validate EVERY allocation */
        perror ("malloc-*matrixPointer");
        return NULL;
    }

    /* you must allocate rows number of cols * sizeof(int) blocks
     * assigning the starting address to each of your allocated pointers.
     * (always use the dereferenced pointer to set type-size)
     */
    for (int i = 0; i < rows; ++i)
    {   /* use calloc on rows to initialize all bytes zero */
        matrixPointer[i] = calloc (sizeof *matrixPointer[i], cols);
        if (!matrixPointer[i]) {                    /* validate EVERY allocation */
            perror ("calloc-matrixPointer[i]");
            while (i--)                             /* on error, free prior memory */
                free (matrixPointer[i]);
            free (matrixPointer);
            return NULL;
        }
    }

    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            while (1) { /* loop continually until valid integer provided */
                printf ("\nPlease enter element for [%d][%d]: \n", i, j);
                
                /* validate EVERY user-input */
                if (scanf("%d", &matrixPointer[i][j]) == 1)
                    break;
                    
                fputs ("  error: invalid integer input.\n", stderr);
            }
        }
    } 
    
    /* move numberOfMatrices++ below the assignment */
    orderedMatricesInfo[numberOfMatrices][0][0] = rows;
    orderedMatricesInfo[numberOfMatrices][0][1] = cols;

    numberOfMatrices++;
    
    // allocateMatrix(rows, cols);     /* why allocate -- that's what you just did?? */

    saveMatrix(matrixPointer, rows, cols);
    
    return matrixPointer;
}

void saveMatrix(int** matrix, int rows, int cols)
{   
    char fullFileName[256];

    sprintf (fullFileName, "matrix-%02d.txt", numberOfMatrices);

    FILE *file = fopen(fullFileName, "ab");
    if (!file) {    /* validate file open for writing */
        perror ("fopen-file");
        return;
    }
    
    for(int i=0; i<rows; ++i)
    {
        fwrite(matrix[i], sizeof *matrix[i], cols, file);
    }
    
    if (fclose(file) == EOF)        /* always validate close after write */
        perror ("fclose-file");
}

void *allocateMatrix(int rows, int cols)
{
    return malloc(sizeof(int[rows][cols]));
}

int **readMatrix(int rows, int cols, int matrix[matrixRows][matrixCols], int matrixNum) 
{
    int **matrixPointer;
    matrixPointer = malloc(sizeof *matrixPointer * rows);   /* rows not cols!! */
    if (!matrixPointer) {                           /* validate EVERY allocation */
        perror ("malloc-*matrixPointer");
        return NULL;
    }

    for (int i =0; i < rows; ++i)                           /* rows not cols!! */
    {
        matrixPointer[i] = calloc (sizeof *matrixPointer[i], cols);
        if (!matrixPointer[i]) {                    /* validate EVERY allocation */
            perror ("calloc-matrixPointer[i]");
            while (i--)                             /* on error, free prior memory */
                free (matrixPointer[i]);
            free (matrixPointer);
            return NULL;
        }
    }

    char fullFileName[256];

    sprintf (fullFileName, "matrix-%02d.txt", matrixNum);

    FILE *file = fopen(fullFileName, "rb");
    
    if (!file) {    /* validate file open for reading */
        perror ("fopen-file");
        for (int i = 0; i < rows; i++)      /* on error free memory */
            free (matrixPointer[i]);
        free (matrixPointer);
        return NULL;
    }
    /* validate EVERY input */
    if (fread (matrix, sizeof(int[rows][cols]), 1, file) < 1) { // read 1 2D-array
        for (int i = 0; i < rows; i++)      /* on error free memory */
            free (matrixPointer[i]);
        free (matrixPointer);
        return NULL;
    }
    
    for (int i = 0; i < rows; ++i)
    {
        memcpy (matrixPointer[i], matrix[i], sizeof *matrixPointer[i] * cols);
        /*
        for (int j = 0; j < cols; ++j)
        {
            matrixPointer[i][j] = matrix[i][j];
            printf("%d", matrixPointer[i][j]);
        }
        */
    }

    // allocateMatrix(rows, cols);  /* not needed */

    return matrixPointer;
}

void displayMatrices()
{
    for (int matrixNum = 0; matrixNum < numberOfMatrices; matrixNum++)
    {
        char fullFileName[256];
        sprintf (fullFileName, "matrix-%02d.txt", numberOfMatrices);
    
        matrixRows = orderedMatricesInfo[matrixNum][0][0];
        matrixCols = orderedMatricesInfo[matrixNum][0][1];

        int matrix[matrixRows][matrixCols];
        int **matrixPointer;

        matrixPointer = readMatrix(matrixRows, matrixCols, matrix, matrixNum + 1);

        displayMatrix(matrixRows, matrixCols, matrixPointer);
    }
}

void displayMatrix(int rows, int cols, int** matrix)
{
    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            printf (" %2d", matrix[i][j]);
        }
        putchar ('\n');
    }
    putchar ('\n');
}

(примечание: в приведенном выше коде оставлен ряд дополнительных комментариев, а не подробное описание каждого предлагаемого изменения в форме абзаца)

Пример использования/вывода

$ ./bin/write_matrix
Please enter the number of rows in your matrix:
2
Please enter the number of columns in your matrix:
2

Please enter element for [0][0]:
1

Please enter element for [0][1]:
2

Please enter element for [1][0]:
3

Please enter element for [1][1]:
4
  1  2
  3  4

SUCCESS

Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.

person David C. Rankin    schedule 11.11.2020
comment
после копирования этого в мой код я все еще получаю ошибку сегментации...? - person Siddharth Chaudhary; 11.11.2020
comment
Ошибка сегментации связана с другими частями вашего кода. Если вы опубликуете остальные, я смогу увидеть, как объявляется и инициализируется numberOfMatrices (предположим, что int и 0), а затем что такое orderedMatricesInfo (предположим, что это трехмерный массив некоторого измерения). Затем я могу проверить остальную часть вашего кода. - person David C. Rankin; 11.11.2020
comment
fwrite(matrix[i], sizeof(int), rows, file); в saveMatrix() имеет ту же проблему rows / cols. Вы пишете cols количество int в строке. - person David C. Rankin; 11.11.2020
comment
Я понимаю. Я только что отредактировал код, чтобы он содержал переменную numberOfMatrices и все - person Siddharth Chaudhary; 11.11.2020
comment
Я определил, что функция displayMatrices - это то, в чем заключается проблема и что вызывает ошибку сегментации... однако я не понимаю, почему - person Siddharth Chaudhary; 11.11.2020
comment
Вероятно, потому что массивы основаны на ZERO в C, поэтому вы хотите, чтобы for (int matrixNum = 0; matrixNum < numberOfMatrices; ++matrixNum) ограничивало ваш цикл. - person David C. Rankin; 11.11.2020
comment
к сожалению, нет, это все еще не решает проблему. Я оставил matrixNum равным 1, чтобы обозначить первую созданную матрицу и, следовательно, первый (1-й) файл. Это не должно вызывать проблем с доступом к элементам, поскольку (matrixNum-1), являющийся индексом, избегает этого. - person Siddharth Chaudhary; 11.11.2020
comment
Я понял, удалив части кода и повторно запустив его, что функция readMatrix может быть источником проблемы, однако я не могу понять, почему - person Siddharth Chaudhary; 11.11.2020
comment
У вас также есть такая же путаница rows / cols в вашей функции readMatrix(). Полное обновление скину через 5 мин. - person David C. Rankin; 11.11.2020
comment
Я скопировал ваш код и запустил его. Это работало для матрицы 4x4. Но когда я запускал его для матрицы 4x5, он не работал и неправильно отображал введенные значения. - person Siddharth Chaudhary; 11.11.2020
comment
Он показал, что вместо {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} - person Siddharth Chaudhary; 11.11.2020
comment
Прочитайте мои расширенные комментарии о том, как ваш формат файла в настоящее время добавляется к тому же файлу. Таким образом, если вы записали 4x4, а затем записали 4x5, ваш код попытается прочитать и отобразить значения из первой матрицы 4x4, захватывая дополнительные целые числа по мере необходимости. Это проблема с открытием файла в режиме "ab" вместо режима "wb", поэтому файл каждый раз перезаписывается. Вы можете сделать это любым способом, но если вы используете "ab", у вас должен быть способ вычислить смещение от начала файла, в который записывается каждая новая матрица. - person David C. Rankin; 11.11.2020
comment
Добро пожаловать -- удачи вам в программировании ! Не забудьте взглянуть на: Что мне делать, когда кто-то ответит на мой вопрос? :) - person David C. Rankin; 11.11.2020