Динамическое выделение неизвестной матрицы в C

Мне нужно взять файл, который вводится пользователем, и умножить его на другой файл. Это то, что я знаю, как сделать.

Проблема в том, что один файл - это массив, а другой - матрица.

Мне нужно отсканировать в первой строке матрицы, чтобы найти размер матрицы, а затем мне нужно динамически выделить матрицу и массив из файлов.

Это то, что я до сих пор:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
int main()
{       
    int row1, col1;
        //These values need to be pulled from the first file//
    char filename1[100];
        //Setting the file name for entry and setting the limit to 100//
    FILE* fp1;
        //FILE must be set as a pointer (FILE must also be capitalized)//

    printf("Enter file name including file extension: \n");
        //This will pull in the name entered by the user//
    scanf("%s", filename1);
        //Scans in the name of the first file//

    fp1 = fopen(filename1, "r");
        //This will open the file as entered by the user//
    if (fp1 == NULL)
    {
        printf("\nError, file not found\n");
        exit(0);
    }
        //This is for the first file//

    char filename2[100];
        //Setting the file name for entry and setting the limit to 100//
    FILE* fp2;
        //FILE must be set as a pointer (FILE must also be capitalized)//

    printf("Enter file name including file extension: \n");
        //This will pull in the name entered by the user//
    scanf("%s", filename2);
        //Scans in the name of the first file//

    fp2 = fopen(filename2, "r");
        //This will open the file as entered by the user//
    if (fp2 == NULL)
    {
        printf("\nError, file not found\n");
        exit(0);
    }
        //This is for the second file//

        //**I need to now dynamically allocate the input files**//

    return 0;
} 

Также извините за то, что я только что ушел после публикации своего вопроса, так как некоторые участники поделились в комментариях, сказав, что я занимался поиском кода. Я не; Я просто не осознавал, насколько активно это сообщество. Спасибо за вклад до сих пор.

Вот скриншот всего, что у меня есть, включая файлы, которые будут прочитаны.

Спасибо за предложения. Мне удалось выяснить функцию "fgets", и я использовал ее, чтобы получить размер матрицы из первого файла. После того, как у меня было это, динамически распределять это было легко.

2 ответа

Решение

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

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

Итак, какие операции вы хотите над своими матрицами?

  • создать матрицу заданных размеров

  • уничтожить ранее созданную матрицу

  • получить доступ к некоторому элементу в данной матрице с указанными индексами строк и столбцов

  • изменить значение элемента в данной матрице с заданными индексами строк и столбцов

  • так далее....

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

Вы должны перечислить - на бумаге или на доске - все необходимые операции с вашими матрицами и объяснить их в своей документации (или в ваших комментариях). На практике у вас может быть много десятков или даже сотен операций над вашим абстрактным типом данных. Документируйте также, что происходит в случаях ошибок.

Я обычно рекомендую сохранять размеры с матрицей (если только вы не знаете, что некоторые размеры являются постоянными). Распространенным способом реализации абстрактных типов данных в C является их инкапсуляция в некоторых struct и использовать указатели на них.

Поэтому я предлагаю использовать гибкий элемент массива (как последний элемент вашего struct). Вот мой matrix_st состав:

  struct matrix_st {
    unsigned m_h, m_w; // height and width of matrix
    double m_v[]; // values inside the matrixes, there are m_h*m_w of them
  };

так что мой абстрактный тип данных просто указатели на

  typedef struct matrix_st Matrix;

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

  Matrix* matrix_create(unsigned height, unsigned width);
  void matrix_destroy(Matrix*mat);
  double matrix_access(Matrix*mat, unsigned i, unsigned j);
  void matrix_change_element(Matrix*mat, unsigned i, unsigned j,double v);

Вот некоторые реализации (так как я не хочу иметь дело с патологически огромными матрицами, я определяю некоторое максимальное измерение; ресурсы компьютера всегда конечны!):

  #define MATRIX_MAXDIM 10000000 /* ten millions */
  Matrix* matrix_create(unsigned height, unsigned width) {
     if (height>MATRIX_MAXDIM || width>MATRIX_MAXDIM) {
        fprintf(stderr, "too huge matrix height=%u width=%u\n",
                height, width);
        exit(EXIT_FAILURE);
     };
     Matrix* res = 
        calloc(1, sizeof(Matrix) + height*width*sizeof(double));
     if (!res) {
         perror("matrix calloc");
         exit(EXIT_FAILURE);
     };
     res->m_h = height;
     res->m_w = width;
     return res; 
  } // end matrix_create

я использую calloc не malloc потому что я действительно хочу немного памяти. Таким образом, возвращаемая матрица содержит все нули. КСТАТИ на некоторых компьютерах (не мой, ПК /Linux/Debian/x86-64 рабочий стол) height*width*sizeof(double) может переполниться.

Вот функция для доступа к некоторому элементу. Это делает некоторую проверку ошибок.

double matrix_access(Matrix*mat, unsigned i, unsigned j) 
{ 
   if (!mat) 
      { fprintf(stderr, "no matrix to access\n"); exit(EXIT_FAILURE; };
   unsigned h = mat->m_h;
   unsigned w = mat->m_w;
   if (i >= h || j >= w)
      { fprintf(stderr, "out-of-bound matrix access\n"); 
        exit(EXIT_FAILURE); };
   return mat->m_v [i*h + j];
}

Так как я сделал только один calloc уничтожение просто закодировать:

  void matrix_destroy(Matrix*mat) {
    if (!mat) { fprintf(stderr, "no matrix to destroy\n"); exit(EXIT_FAILURE); };
    assert (mat->m_h < MATRIX_MAXDIM);
    assert (mat->m_w < MATRIX_MAXDIM);
    free (mat);
  }

assert заявления в принципе бесполезны (они проверяют что-то, что всегда должно быть правдой). Но я люблю защитное программирование (это поможет мне ловить ошибки в некоторых других местах, неправильно используя мои Matrix). Они могут быть отключены (читайте assert (3)) во время компиляции.

Кстати, вы могли бы объявить эти функции как inline или же static inline (и определите их в некотором заголовочном файле). Оптимизирующий компилятор может создавать эффективный код (например, компилировать с gcc -O2 -Wall -march=native когда бенчмаркинг).

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


Кодирование других функций оставлено читателю в качестве упражнения.

Не забудьте собрать все предупреждения и отладочную информацию, поэтому gcc -Wall -Wextra -g с GCC. Используйте отладчик gdb (а также valgrind охотиться на утечки памяти). Прочитайте документацию по каждой используемой функции (например, ваш код не проверяет счетчик возвращаемых значений scanf но это действительно должно). Запустите несколько тестовых случаев. Попытайтесь убедить себя, что ваш код хорош (доказывая его части). Возможно, используйте некоторый статический анализатор исходного кода (например, Frama-C, который хочет дополнительные аннотации в ACSL). Если вам нужно сравнить свою программу, включите оптимизацию во время компиляции (например, передав -O2 -march=native в gcc....).


В кодовом комментарии вы спрашиваете:

 // I need to now dynamically allocate the input files

Вы не распределяете входные файлы ( операционная система управляет ими), вы выделяете некоторую зону памяти. Читайте о динамическом распределении памяти C. Обратите внимание, что выделение памяти может произойти сбой (например, как описано в malloc (3)), потому что ваше виртуальное адресное пространство не может расти бесконечно.

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

Я не вижу, где вы на самом деле читаете количество строк / столбцов, но как только они у вас есть, распределение просто:

int (*matrix)[columnCount] = malloc(rowCount*sizeof(*matrix));

Вот и все. Это заявляет matrix быть указателем *matrix к массиву из columnCount целые числа. Скобки необходимы, потому что int* matrix[...] объявил бы массив указателей вместо этого. malloc() выделяет место для rowCount такие массивы, давая вам полную 2D-матрицу в одной части памяти. Доступ как для любого 2D-массива:

for(int y = 0; y < rowCount; y++) {
    for(int x = 0; x < columnCount; x++) {
        matrix[y][x] = 42;
    }
}

Распределение так же просто, как распределение:

free(matrix);
Другие вопросы по тегам