Передача многомерных массивов в качестве аргументов функции в C

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

Кроме того, мой многомерный массив может содержать типы, отличные от строк.


person David    schedule 06.08.2008    source источник


Ответы (5)


Вы можете сделать это с любым типом данных. Просто сделайте его указателем на указатель:

typedef struct {
  int myint;
  char* mystring;
} data;

data** array;

Но не забывайте, что вам все еще нужно выделить переменную, и это становится немного сложным:

//initialize
int x,y,w,h;
w = 10; //width of array
h = 20; //height of array

//malloc the 'y' dimension
array = malloc(sizeof(data*) * h);

//iterate over 'y' dimension
for(y=0;y<h;y++){
  //malloc the 'x' dimension
  array[y] = malloc(sizeof(data) * w);

  //iterate over the 'x' dimension
  for(x=0;x<w;x++){
    //malloc the string in the data structure
    array[y][x].mystring = malloc(50); //50 chars

    //initialize
    array[y][x].myint = 6;
    strcpy(array[y][x].mystring, "w00t");
  }
}

Код для освобождения структуры выглядит аналогично — не забудьте вызвать free() для всего, что вы выделили! (Кроме того, в надежных приложениях следует проверить возврат функции malloc(). .)

Теперь предположим, что вы хотите передать это функции. Вы все еще можете использовать двойной указатель, потому что вы, вероятно, хотите выполнять манипуляции со структурой данных, а не с указателем на указатели структур данных:

int whatsMyInt(data** arrayPtr, int x, int y){
  return arrayPtr[y][x].myint;
}

Вызовите эту функцию с помощью:

printf("My int is %d.\n", whatsMyInt(array, 2, 4));

Вывод:

My int is 6.
person andrewrk    schedule 07.08.2008
comment
здесь нужна помощь: stackoverflow.com/questions/ 16943909/ - person Dchris; 05.06.2013
comment
Указатель на сегментированную таблицу поиска указателя не является двумерным массивом. Просто потому, что он допускает синтаксис [][], он не превращается волшебным образом в массив. Вы не можете memcpy() и т. д., потому что память не выделяется в соседних ячейках памяти, что требуется для массивов. Ваша таблица поиска довольно разбросана по всей куче, что делает поиск медленным, а куча фрагментирована. - person Lundin; 23.06.2015

Передайте явный указатель на первый элемент с размерами массива в качестве отдельных параметров. Например, для обработки двумерных массивов произвольного размера int:

void func_2d(int *p, size_t M, size_t N)
{
  size_t i, j;
  ...
  p[i*N+j] = ...;
}

который будет называться

...
int arr1[10][20];
int arr2[5][80];
...
func_2d(&arr1[0][0], 10, 20);
func_2d(&arr2[0][0], 5, 80);

Тот же принцип применяется для массивов более высокой размерности:

func_3d(int *p, size_t X, size_t Y, size_t Z)
{
  size_t i, j, k;
  ...
  p[i*Y*Z+j*Z+k] = ...;
  ...
}
...
arr2[10][20][30];
...
func_3d(&arr[0][0][0], 10, 20, 30);
person John Bode    schedule 16.12.2009
comment
Вместо p[i*Y+j*Z+k] должно быть p[i*Y*Z+j*Z+k]. - person David H; 18.08.2012
comment
каковы значения i и j? - person AlphaGoku; 06.04.2016

Вы можете объявить свою функцию как:

f(int size, int data[][size]) {...}

Затем компилятор сделает всю арифметику указателя за вас.

Обратите внимание, что размеры размеров должны стоять перед самим массивом.

GNU C позволяет пересылать объявления аргументов (на случай, если вам действительно нужно передать размеры после массива):

f(int size; int data[][size], int size) {...}

Первое измерение, хотя вы также можете передать его в качестве аргумента, бесполезно для компилятора C (даже для оператора sizeof, когда он применяется к массиву, переданному в качестве аргумента, всегда будет рассматриваться как указатель на первый элемент).

person rslemos    schedule 08.07.2014
comment
ИМО, это должен быть принятый ответ. Нет необходимости в дополнительном коде и ненужном выделении кучи. простой и чистый - person kjh; 27.04.2015
comment
Спасибо @kjh, я также думаю, что это самое чистое решение. Принятый ответ - тот, который сработал для него. Посмотрите: ОП из 2008 года, почти за 6 лет до моего ответа. Кроме того, я не знаю, допускали ли тогда стандарты C синтаксис, который я использовал здесь. - person rslemos; 30.04.2015
comment
Это решение, которое я наконец принял для передачи целочисленной матрицы (двумерного массива) размера M x N в качестве аргумента функции. Возможно, будет полезно немного больше информации: Прототип функции выглядит так: void f(int N, int data[][N], int M); В теле функции элемент [m][n] можно записать как data[m][n] — очень удобно, вычисление индекса не требуется. - person jonathanzh; 19.05.2015
comment
Я объявляю функцию, как вы сказали, я вызываю ее из main(), и все в порядке, но как мне объявить переменную data в моей main(), если я не знаю размеров? Я пытался с int* data, но не работает. - person glc78; 27.08.2017
comment
@ glc78 Либо как VLA в стеке int data[height][width];, либо в куче с int (*data)[width] = malloc(height*sizeof(*data));. В обоих случаях вы можете впоследствии получить доступ к data[y][x] обычным способом и передать его f(width, data). - person cmaster - reinstate monica; 13.12.2019

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

Нет

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

Вы можете поместить измерение [s] в структуру вместе с массивом и заявить, что передаете «один аргумент», но на самом деле это просто упаковка нескольких значений в один контейнер и вызов этого контейнера «один аргумент».

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

void print2dIntArray( size_t x, size_t y, int array[ x ][ y ] )
{
    for ( size_t ii = 0, ii < x; ii++ )
    {
        char *sep = "";
        for ( size_t jj = 0; jj < y; jj++ )
        {
            printf( "%s%d", sep, array[ ii ][ jj ] );
            sep = ", ";
        }
        printf( "\n" );
    }
}

Вы бы назвали эту функцию следующим образом:

int a[ 4 ][ 5 ];
int b[ 255 ][ 16 ];

...

print2dIntArray( 4, 5, a );

....

printt2dIntArray( 255, 16, b );

Точно так же трехмерный массив, например, struct pixel:

void print3dPixelArray( size_t x, size_t y, size_t z, struct pixel pixelArray[ x ][ y ][ z ] )
{
    ...
}

или одномерный массив double:

void print1dDoubleArray( size_t x, double doubleArray[ x ] )
{
    ...
}

НО...

Однако можно передавать «массивы указателей на массивы указателей на... массив типа X», которые часто ошибочно называют «многомерным массивом», в качестве одного аргумента, если базовый тип X имеет сигнальное значение, которое можно использовать для указания конца конечного одномерного массива самого низкого уровня типа X.

Например, значение char **argv, переданное в main(), является указателем на массив указателей на char. Исходный массив из char * указателей заканчивается NULL сигнальным значением, а каждый массив char, на который ссылается массив из char * указателей, заканчивается NUL символьным значением '\0'.

Например, если вы можете использовать NAN в качестве контрольного значения, поскольку фактические данные никогда не будут равны NAN, вы можете напечатать double ** следующим образом:

void printDoubles( double **notAnArray )
{
    while ( *notAnArray )
    {
        char *sep = "";
        for ( size_t ii = 0;  ( *notAnArray )[ ii ] != NAN; ii++ )
        {
            printf( "%s%f", sep, ( *notAnArray )[ ii ] );
            sep = ", ";
        }

        notAnArray++;
    }
}
person Andrew Henle    schedule 13.12.2019