Манипулировать многомерный массив в функции
Я прочитал много вещей здесь и попробовал много, но я не мог найти способ передать многомерный массив функции в 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 }