Как работать с двумерным массивом с неизвестными размерами?

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

Как найти их размеры и как вернуть массив, когда я не знаю размеров продукта во время компиляции?


person Andreas    schedule 04.05.2012    source источник
comment
Или мне должно передавать размер в качестве отдельного аргумента?   -  person Andreas    schedule 04.05.2012
comment
Единственный способ узнать это — передать размеры массива в функцию в качестве параметров.   -  person Mahesh    schedule 04.05.2012


Ответы (6)


Вы не можете узнать размеры массива, если все, что у вас есть, это указатель на начало массива. Вам нужно будет передать размеры массива в functions.

person David Heffernan    schedule 04.05.2012

Этот ответ является полным излишеством, но это мой подход, основанный на struct, который n.m. называется «целая «другая история».

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

В приведенном ниже коде показан один из возможных подходов. Обратите внимание, что здесь есть пара «трюков». Во-первых, все данные хранятся в одном непрерывном блоке — это может улучшить производительность по ряду причин. Одним из потенциальных недостатков этого метода является то, что изменение размера матрицы становится дорогостоящим, поскольку вам приходится выделять совершенно новый экземпляр и копировать данные. Но если вы обнаружите, что это проблема, вы всегда можете изменить свою реализацию, предполагая, что вы всегда используете функции matrix_get() и matrix_set() для доступа к значениям в матрице.

Кроме того, матричная структура и память, на которую указывает указатель данных, выделяются в одном вызове malloc. Если вы используете этот метод, просто помните о проблемах с выравниванием данных. Например, если вы измените данные, чтобы они указывали на 64-битные целые или двойные числа, вам нужно будет добавить заполнение, чтобы убедиться, что все выровнено по 8 байтам. В качестве альтернативы просто malloc указатель данных как отдельный массив в функции new_matrix(), при условии, что вы не забудете освободить его в free_matrix().

Я оставил в качестве упражнения для ОП, чтобы написать функцию умножения.

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

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

struct matrix * new_matrix( int rows, int cols )
{
   struct matrix * m = NULL;

   /* Allocate a block of memory large enough to hold the matrix 'header' struct
    * as well as all the row/column data */
   m = malloc( sizeof(struct matrix)  + (rows * cols * sizeof(int) ) );

   if( m )
   {
      m->rows = rows;
      m->cols = cols;

      /* Some ugly pointer math to get to the first byte of data */
      m->data = (int*) ( (char *) m  + sizeof(*m) );
   }
   return m;
}

void free_matrix( struct matrix * m )
{
   free( m );
}

int matrix_set( struct matrix * m, int row, int col, int val)
{
   if( col >= m->cols || row >= m->rows )
      return -1;
   m->data[ m->cols * row + col ] = val;
   return 0;
}

int matrix_get( struct matrix * m, int row, int col, int * val)
{
   if( col >= m->cols || row >= m->rows )
      return -1;
   else 
   {
      *val = m->data[ m->cols * row + col ];
      return  0;
   }
}

void print_matrix( struct matrix * m )
{
   int r,c;
   int val;
   for( r = 0; r < m->rows; r++ )
   {
      for( c = 0; c < m->cols; c++ )
      {
         matrix_get( m, r, c, &val );
         printf( "%5d%s", val, c + 1 < m->cols ? "," : "" );
      }
      printf("\n");
   }
}

int main (int argc, char **argv)
{
   int r,c;
   struct matrix * m = new_matrix( 5, 5 );

   for(  r = 0; r < m->rows; r++ )
   {
      for( c = 0; c < m->cols; c++ )
      {
         matrix_set( m, r, c, (r +1)* 10 + c + 1 );
      }
   }

   print_matrix( m );
   free_matrix( m );
   return 0;
}
person Brian McFarland    schedule 04.05.2012

В C нет настоящих 2D-массивов. Есть только массивы массивов, что не совсем одно и то же. (Я знаю, что меня побьют за эти слова. Потерпите меня.)

Хотя разница может показаться не очень существенной, она есть. Чтобы работать с массивом, вам не нужно знать его размер во время компиляции. Например

double dot_product (double a[], double b[], int size); 
/* an A-OK function */

Однако вы должны знать размер любого элемента массива. Например

void matrix_product (double a[][], double b[][], double result[][], 
                     int a_rows, int a_cols, int b_cols);
/* Bad, would not compile. You are only permitted to say "double[N][]", 
   where N is known at compile time */

Если вам нужен полностью общий код манипулирования матрицами, вам нужно использовать простой одномерный массив и самостоятельно вычислить индекс из номеров строк и столбцов. Вы также должны передавать размеры.

void matrix_product (double a[], double b[], double result[], 
                     int a_rows, int a_cols, int b_cols) {
  ...
  for (col = 0; col < a_cols; ++col) {
    for (row = 0; row < a_rows; ++row) {
      ...
      ... a[row*a_cols + col] ...
      ...

В этом примере вызывающая сторона выделяет result, а не функцию умножения.


На более высоком уровне вы захотите инкапсулировать матрицы вместе с их измерениями в какой-либо форме абстрактного матричного типа данных, например так:

struct matrix {
  double* elements;
  int rows, cols;
};

но это совсем другая история.

person n. 1.8e9-where's-my-share m.    schedule 04.05.2012
comment
Я согласен, что один массив - это путь. Ответ Догберта показывает другой путь. См. мой ответ для примера того, как сделать инкапсуляцию в структуре. - person Brian McFarland; 04.05.2012

Вам лучше всего создать структуру MATRIX для вашего массива. Как отметил @David, в C вы должны сами отслеживать размеры. В языке НЕТ встроенных функций, которые сделают это за вас безопасно.

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

Попробуй это:

typedef struct matrix {
  int **array;
  int rows;
  int cols;
} matrix_t;


int createMatrix(matrix_t* mtx) {
  int i;
  if (!mtx) {
    printf("INVALID POINTER TO MATRIX");
    return -1;
  }
  if ( (mtx->rows == 0) || (mtx->cols == 0) ) {
    printf("Rows/columns cannot be zero!");
    return -1;
  }
  int status = 0;
  // allocate the array
  mtx->array = (int **)calloc(mtx->rows,sizeof(int*));
  if(mtx->array != NULL) {
    // memory allocation succeeded
    for(i = 0; i < mtx->rows; i++) {
      if((mtx->array[i] = (int*)calloc(mtx->cols,sizeof(int))) == NULL) {
        printf("Memory allocation error");
        status = -1;
        break;
      } else {
        // Allocation successful
      }
    }
  } else {
    printf("Memory allocation error!");
    status = -1;
  }
  return status;
}

int destroyMatrix(matrix_t* mtx) {
  // destroy the array
  for(i = 0; i < mtx->rows; i++) {
    if(mtx->array[i] != NULL) {
      free(mtx->array[i]);
      mtx->array[i] = NULL;
    }
  }
  if(mtx->array) {
    free(mtx->array);
    mtx->array = NULL;
  }
  return 0;
}

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

  matrix_t myMtx;
  myMtx.array = NULL;
  myMtx.rows = 3;
  myMtx.cols = myMtx.cols; // Make it a square matrix
  if(createMatrix(&myMtx) == 0 ) {
    printf("Created matrix successfully");
  } else {
    printf("Failed to create matrix!");
  }

Эти функции также любезно проверяют, не удалось ли выделить память, и избегают сбоя программы (например, SEGFAULT), проверяя все указатели перед их использованием.

Удачи!

person Cloud    schedule 04.05.2012
comment
Если вы выберете этот подход, я бы, по крайней мере, передал параметры строк и столбцов в createMatrix(), так как вам всегда придется инициализировать их, прежде чем вы все равно вызовете createMatrix(). - person Brian McFarland; 04.05.2012
comment
Казалось бы, разные стилистические решения. Я предпочитаю, чтобы структура была автономной, а функция create выполняла соответствующие проверки. При работе с большими переменными контекста имеет смысл сохранять прототипы функций небольшими и читабельными, а не длинными, ИМХО. - person Cloud; 06.05.2012
comment
Да, я думаю, это хороший момент, чтобы прототип функции был коротким и приятным. Я больше думал с точки зрения сокрытия/инкапсуляции данных. - person Brian McFarland; 08.05.2012
comment
Тоже разумный подход. У меня больше опыта работы с C/ассемблером, поэтому, если что-то скрыто, библиотека предварительно собрана, а общедоступные материалы помещаются в один заголовочный файл. C++ и Java более благоприятны для сокрытия данных и в этом случае позволяют привести более хороший пример. - person Cloud; 08.05.2012

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

Попробуйте что-нибудь в этом роде, где A — матрица размера n x m, а B — матрица размера m x p:

int* matix_multiply(int* matrix_a, int* matrix_b, int n, m, int p){

    int *result = (int *) malloc(sizeof(int) * a_rows * b_cols);
    if(result == NULL)
        exit(EXIT_FAILURE);
    //loops to do the actual multiplication and store the results in result[i][j]
    return result; //returns the result array
}
person seanwatson    schedule 04.05.2012

#include <stdio.h>

void foo(int *a, int m, int n)
{
    for(int i=0; i<m; i++)
        for(int j=0; j<n; j++)
            printf("%d\n", *(a+i*n+j)); 
}
int main()
{
    int a[][2] = {{1,2},{3,4}};
    foo((int *)a,2,2);
    return 0;
}

обычно двумерные массивы хранятся в виде строк. А 2D-массив можно рассматривать как массив указателей, указывающих на массив элементов. Поскольку мы также передаем два измерения, мы можем получить доступ к элементам соответствующим образом.

даже вы можете попробовать другие типы данных. ниже я использовал структуру.

#include <stdio.h>
struct abc
{
    int a;
    float b;
    char c;
};
void foo(struct abc *a, int m, int n)
{
    for(int i=0; i<m; i++)
        for(int j=0; j<n; j++)
            printf("%d %f %c\n", (a+i*n+j)->a, (a+i*n+j)->b, (a+i*n+j)->c); 
}
int main()
{
    struct abc a[][2] = {{{1,1.0,'a'},{2,2.0,'b'}}, {{3,3.0,'c'},{4,4.0,'d'}}};
    foo((struct abc *)a,2,2);
    return 0;
}

Надеюсь, поможет....!!!!!

person mayur bhoraniya    schedule 11.08.2017
comment
Вы должны добавить некоторые комментарии, чтобы объяснить, как ваш код решает вопрос. - person moggi; 11.08.2017
comment
Пожалуйста, уточните свой ответ - person UmarZaii; 11.08.2017