Вы правы в том, что C не позволяет нам возвращать массивы из функций. Это одна из областей, где С просто плох, и вы будете выбирать между различными пороками.
Наиболее очевидные альтернативы - возвращать указатель массива или указатель void
.
void
следует избегать, поскольку они имеют несуществующий тип безопасности.
// bad code
void* cofactor (int A[100][100], int n, size_t r, size_t c)
Опция указателя массива довольно уродливая, трудно читаемая и обеспечивает размеры фиксированного размера:
// bad code
int ( *cofactor (int A[100][100], int n, size_t r, size_t c) )[100][100];
В качестве альтернативы, уродливой и плохой практикой, является скрытие типа массива за typedef:
// bad code
typedef int arr_t [100][100];
arr_t* cofactor(int A[100][100], int n, size_t r, size_t c)
Версии указателя массива также имеют ограничение, что вы не можете использовать переменные измерения. Но r
и c
здесь кажутся строками и столбцами, поэтому вы, вероятно, do хотите, чтобы массив имел переменный размер.
Здесь некоторые начинают использовать int**
в замешательстве. Но int**
нельзя использовать для указания на 2D-массив или на первый элемент 2D-массива. Его можно использовать для указания на первый элемент одномерного массива указателей int*
, а затем эмулировать что-то, что выглядит как массив, но не ведет себя как один. Это не то, что вы хотите здесь, потому что это медленно и опасно. См. Правильное размещение многомерных массивов .
Вздох. Так что же использовать!
Если вы отбросите требование «function return
ing array» (с акцентом на использование return
), это станет проще и более гибким. Передача параметров в / из функций в C чаще всего осуществляется через параметры, и большинство надежных API-интерфейсов резервируют возвращаемое значение для типа ошибки, описывающего результат функции.
Большим преимуществом здесь является то, что при передаче массива в качестве параметра мы можем использовать переменные измерения:
void func (size_t r, size_t c, int A[r][c])
Внезапно вы можете получить функцию, принимающую любой размер массива, и несколько безопасную для типов, если r
и c
имеют правильные значения.
Самое чистое - это оставить выделение вызывающей стороне. Тогда вы получите
void func (size_t r, size_t c, int A[r][c], int B[r][c])
Из всех обсуждаемых вариантов это единственный симпатичный. Но это не сработает, если функция должна выполнить распределение. Затем мы должны вернуть массив через параметр. И к этому с этим синтаксисом, тоже немного уродливо:
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
Но если мы можем жить с этим странно выглядящим «указателем на массив указателем на массив int[r][c]
», то это решает все проблемы. Он может вернуть массив переменной размера из функции вызывающей стороне.
Функция, создающая копию любого массива и возвращающая его, будет выглядеть так:
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
*B = malloc( sizeof(int[r][c]) );
int (*b)[c] = **B; // pointer to the first row in an array int[r][c]
for(size_t i=0; i<r; i++)
{
for(size_t j=0; j<c; j++)
{
b[i][j] = A[i][j];
}
}
}
Или, если хотите:
#include <string.h>
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
*B = malloc( sizeof(int[r][c]) );
memcpy( *B, A, sizeof(int[r][c]) );
}
Полный пример:
#include <stdlib.h>
#include <stdio.h>
void copy (size_t r, size_t c, int (**B)[r][c], int A[r][c])
{
*B = malloc( sizeof(int[r][c]) );
int (*b)[c] = **B; // pointer to the first row in an array int[r][c]
for(size_t i=0; i<r; i++)
{
for(size_t j=0; j<c; j++)
{
b[i][j] = A[i][j];
}
}
}
int main (void)
{
int array1[2][3] = { {1,2,3}, {4,5,6} };
int (*array2)[2][3];
copy(2, 3, &array2, array1);
int (*arr)[3] = *array2;
for(size_t i=0; i<2; i++)
{
for(size_t j=0; j<3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
free(array2);
}