C не рассматривает массивы, как и большинство языков; Вы должны понимать следующие концепции, если вы хотите работать с массивами в С.
исключением случаев, когда это операнд оператора sizeof
или унарный &
, или строковый литерал используется для инициализации другого массива в Объявление выражение типа «N-элементный массив T
» будет преобразовано («распад») в выражение типа «указатель на T
», а значение выражения будет адресом первого элемента массив. Этот результат не является значением lvalue; он не может быть целью присвоения, а также не может быть операндом для операторов или --
.
Вот почему вы не можете определить функцию для возврата типа массива; выражение массива будет преобразовано в тип указателя как часть инструкции return
, и, кроме того, в любом случае нельзя назначать результат другому выражению массива.
Верьте или нет, есть твердая техническая причина для этого; когда он изначально разрабатывал C, Деннис Ритчи заимствовал много понятий с языка программирования B. Б был «беспричинным» языком; все было сохранено как неподписанное слово или «ячейка». Память рассматривалась как линейный массив «ячеек». Когда вы объявили массив как
auto arr[N];
В будет выделено N «ячеек» для содержимого массива, наряду с дополнительной ячейки, связанной с arr
, чтобы сохранить смещение первого элемента (в основном указатель, но без каких-либо тип семантики).Доступ к массивам был определен как *(arr+i)
; вы смели i
ячейки с адреса, хранящегося в a
, и разыменовали результат. Это отлично поработало для C, пока Ritchie не начал добавлять типы структур к языку. Он хотел, чтобы содержимое структуры не только описывало данные в абстрактных терминах, но и физически представляло биты. Пример, который он использовал что-то вроде
struct {
int node;
char name[14];
};
Он хотел выделить 2 байта для узла, сразу за которым следует 14 байт для элемента имени. И он хотел, чтобы массив таких структур был выложен таким образом, что у вас было 2 байта, за которыми следуют 14 байт, за которыми следуют 2 байта, за которыми следуют 14 байтов и т. Д. Он не мог найти хороший способ справиться с указателем массива, поэтому он полностью избавился от него. Вместо того, чтобы выделять хранилище для указателя, C просто вычисляет его из самого выражения массива. Вот почему вы не можете назначить что-либо для выражения массива; нет ничего, чтобы назначить значение на.
Итак, как вы возвращаете 2D-массив из функции?
У вас нет. Вы можете вернуть указатель к 2D массива, например:
T (*func1(int rows))[N]
{
T (*ap)[N] = malloc(sizeof *ap * rows);
return ap;
}
Недостатком этого подхода является то, что N
должны быть известны во время компиляции.
Если вы используете C99 компилятор или C2011 компилятор, который поддерживает массивы переменной длины, вы могли бы сделать что-то вроде следующего:
void func2(size_t rows, size_t cols, int (**app)[cols])
{
*app = malloc(sizeof **app * rows);
(*app)[i][j] = ...; // the parens are necessary
...
}
Если вы не имеете переменной длины массивы доступны, то, по крайней мере, размер столбца должен быть константой во время компиляции:
#define COLS ...
...
void func3(size_t rows, int (**app)[COLS])
{
*app = malloc(sizeof **app * rows);
(*app)[i][j] = ...;
}
Вы можете выделить память по частям в то, что действует как 2D массив, но строки не обязательно должны быть смежными:
int **func4(size_t rows, size_t cols)
{
int **p = malloc(sizeof *p * rows);
if (p)
{
for (size_t i = 0; i < rows; i++)
{
p[i] = malloc(sizeof *p[i] * cols);
}
}
return p;
}
p
является не массив; он указывает на ряд указателей на int
. Для всех практических целей, вы можете использовать это, как если бы это была 2D массив:
int **arr = foo(rows, cols);
...
arr[i][j] = ...;
printf("value = %d\n", arr[k][l]);
Обратите внимание, что C не имеет никакого сбора мусора; вы отвечаете за очистку своих беспорядков. В первых трех случаях, это просто:
int (*arr1)[N] = func(rows);
// use arr[i][j];
...
free(arr1);
int (*arr2)[cols];
func2(rows, cols, &arr2);
...
free(arr2);
int (*arr3)[N];
func3(rows, &arr3);
...
free(arr3);
В последнем случае, так как вы сделали выделение в два этапа, что вам нужно сделать два шага Deallocation:
int **arr4 = func4(rows, cols);
...
for (i = 0; i < rows; i++)
free(arr4[i])
free(arr4)
Я всегда хотел слушайте, как C является вызовом опытному программисту, не использующему программирование. :) – this
Да. Иногда мне жаль, что я не пришел из мира программирования низкого уровня к высокоуровневому. Но, черт возьми, я пытаюсь это сделать сейчас! : D –
Что вы подразумеваете под «константой»? –