Манипулировать многомерный массив в функции

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

Я хотел бы найти способ передать массив функции. Затем передайте ее от первой функции ко второй, сделайте что-нибудь (возможно, напечатайте, возможно измените значения), а затем снова используйте ее для первой функции и, наконец, использовать этот массив в основном.

Моя последняя попытка:

void func(int multarray[][columns]){
    multarray[0][0]=9;
}

int main(){
    int rows;
    int columns;
    int multarray[rows][columns];
    func(multarray);
    return 0;
}

Я также пробовал это:

void func(int multarray[rows][columns]){
    multarray[0][0]=9;
}

int main(){
    int rows;
    int columns;
    int multarray[rows][columns];
    func(multarray);
    return 0;
}

Я также пробовал это:

int
getid(int row, int x, int y) {
          return (row*x+y);
}

void
printMatrix(int*arr, int row, int col) {
     for(int x = 0; x < row ; x++) {
             printf("\n");
             for (int y = 0; y <col ; y++) {
                 printf("%d  ",arr[getid(row, x,y)]);
             }
     }
}

main()
{

    int arr[2][2] = {11,12,21,22};
    int row = 2, col = 2;

    printMatrix((int*)arr, row, col);

}

из здесь

Я также пробовал двойные указатели. Я также читал, что существует другой подход, если компилятор не поддерживает VLA. Я использую gnu.

Ответ 1

Не совсем уверен, что проблема, но это работает (и печатает значение "9" ):

#include <stdio.h>

#define ROWS 10
#define COLUMNS 10

void func2(int multarray[][COLUMNS]){
        multarray[1][4]=10;
}

void func1(int multarray[][COLUMNS]){
        multarray[0][3]=9;
        func2(multarray);
}

int main(){

        int multarray[ROWS][COLUMNS];
        func1(multarray);
        printf("%d\n", multarray[0][3]);
        printf("%d\n", multarray[1][4]);
        return 0;
}

Обратите внимание, что массив переходит в указатель при передаче функции.

Ответ 2

Несколько вещей, которые нужно запомнить:

  • Когда вы передаете выражение массива в качестве аргумента функции, оно будет преобразовано из выражения типа "N-element array of T" в "указатель на T", а значение выражения будет адресом первого элемента массива. Вызываемая функция получает значение указателя.

  • Оператор [] может использоваться с выражениями типа массива или указателя; IOW, учитывая объявления int a[10]; int *p = a;, то p[i] и a[i] относятся к одному и тому же элементу.

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

Итак, для функции, которая управляет 2D VLA, вы должны написать что-то вроде

void foo( size_t rows, size_t cols, int (*multiarray)[cols] ) // or multiarray[][cols]
{
   size_t i, j;

   for ( i = 0; i < rows; i++ )
     for ( j = 0; j < cols; j++ )
       multiarray[i][j] = some_value();
}

Что с int (*multiarray)[cols]? Помните, что при передаче выражения массива в качестве аргумента тип выражения массива преобразуется из "N-element array of T" в "указатель на T". В этом случае T представляет собой "cols -элементный массив int", поэтому мы переходим от "rows -элементного массива cols -element aray от int" до "указателя на cols -элементный массив int". В контексте объявления параметра функции T a[N], T a[] и T *a все идентичны; во всех трех случаях a объявляется как указатель на T. Итак, int (*multiarray)[cols] эквивалентно int multiarray[][cols], что эквивалентно int multiarray[rows][cols]. Я предпочитаю использовать первую форму, потому что она наиболее точно отражает ситуацию.

Если вы хотите передать этот массив в качестве аргумента другой функции, вы должны использовать тот же тип:

void bar( size_t rows, size_t cols, int (*multiarray)[cols] )
{
   foo( rows, cols, multiarray );
}

int main( void )
{
  size_t rows = 0;
  size_t cols = 0;

  // you must assign values to rows and cols before declaring a VLA with them
  rows = ...;
  cols = ...;

  int arr[rows][cols];

  bar( rows, cols, arr );
  ...
}

Любые изменения в содержимом массива, сделанные в foo, будут отражены в bar и main.

VLAs могут быть полезны, но у них есть свои ограничения. Они не могут быть объявлены static, и они не могут быть определены вне функции. Они не могут использовать синтаксис инициализации {}. Кроме того, поддержка VLA теперь является необязательной с 2011 года, поэтому вы не можете полагаться на то, что они поддерживаются повсюду.

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

void foo( size_t rows, size_t cols, int **multiarray )
{
  size_t i, j;

  for ( i = 0; i < rows; i++ )
    for ( j = 0; j < cols; j++ )
      multiarray[i][j] = some_value();

}

void bar( size_t rows, size_t cols, int **multiarray )
{
  foo( rows, cols, multiarray );
}

int main( void )
{
  size_t rows; 
  size_t cols;
  int **multiarray = NULL;

  ... // get rows and cols

  // allocate memory for pointers to each row
  multiarray = malloc( sizeof *multiarray * rows );
  if ( multiarray )
  {
    size_t i;
    // allocate each row
    for ( i = 0; i < rows; i++ )
    {
      multiarray[i] = malloc( sizeof *multiarray[i] * cols );
      if ( !multiarray[i] )
        break;
    }

    if ( i < rows )
    {
      // malloc failed for one of the multiarray rows; we need to 
      // free whatever memory has already been allocated and exit
      while ( i-- )
        free( multiarray[i] );
      free( multiarray );
      exit(0);
    }
  }

  bar ( rows, cols, multiarray );
  ...

  if ( multiarray )
  {
    size_t i;

    for ( i = 0; i < rows; i++ )
      free( multiarray[i] );
    free( multiarray );
  }
}

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

void foo( size_t rows, size_t cols, int *fakemultiarray )
{
  size_t i, j;

  for ( i = 0; i < rows; i++ )
    for ( j = 0; j < rows; j++ )
       fakemultiarray[ i * rows + j ] = some_value();
}

void bar( size_t rows, size_t cols, int *fakemultiarray )
{
  foo( rows, cols, fakemultiarray );
}

int main( void )
{
  size_t rows;
  size_t cols;
  int *fakemultiarray = NULL;

  ... // get rows and cols

  fakemultiarray = malloc( sizeof *fakemultiarray * rows * cols );
  if ( fakemultiarray )
    bar( rows, cols, fakemultiarray );

  ...
  free( fakemultiarray );
}

В этом случае мы выделили один буфер, достаточно большой для всех элементов, но мы должны индексировать его как 1D-массив, вычисляя индекс как i * rows + j.

Ответ 3

Для ваших 2-мерных массивов я бы определил тип для него.

typedef int my2DArray_t[ROWS][COLUMNS];

Затем вы можете объявить переменные этого типа и указатели на них. Это облегчает передачу вещей.

void someFuncOther (my2DArray_t *someArray)
    {

    /* Set some values in array */
    (*someArray)[1][1] = 4;

    }

void someFunc (my2DArray_t *someArray)
    {

    /* Set some values in array */
    (*someArray)[1][0] = 7;

    /* Have someFuncOther do some more work */
    someFuncOther (someArray);

    }


int main (void)
    {
    /* This is the actual array */
    my2DArray_t myArray;

    /* Set some values in array */
    myArray[0][2] = 6;

    /* Have someFunc do some work on myArray by passing a pointer to it */
    someFunc (&myArray);

    }

Ответ 4

Полезно рассматривать массивы как указатели на память. Тогда легко представить 2d-массив как указатель на память указателей (kinda)

Это не сработает

 int arr[2][2] = {11,12,21,22}; //not going to work

но это сработало для меня отлично...

 1 #include <stdio.h>
 2 
 3  
 4 main()
 5 {
 6    int arr[2][2] = {{11,1},{2,21}};
 7    int row = 2, col = 2;
 8    int i,j;
 9 
 10    for(i=0;i<row;i++)
 11       for(j=0;j<col;j++)
 12         printf("%d  - ",arr[i][j]);
 13 
 14 
 15  }