Честно говоря, меня смущают указатели и массивы. Мне потребовалось много времени, чтобы понять, как передать двумерный массив.
Эта путаница очень распространена. Это связано с тем, что вы не можете передавать массивы в C, но язык допускает синтаксис, который выглядит так, как вы могли бы.
Итак, поскольку вы не можете передать массив в функцию, вместо этого вы передаете ему указатель на первый элемент массива и, при необходимости, размер массива. Внутри функции вы можете использовать этот указатель для доступа к массиву, и синтаксис выглядит так же, как если бы это был реальный массив. Это также требует пояснения:
[]
, оператор индексации, работает путем добавления индекса к заданному указателю и разыменования полученного указателя. Поэтому написание a[5]
точно так же, как *(a+5)
.
- Это работает даже в том случае, если
a
является фактическим массивом (а не указателем), потому что массив в большинстве случаев оценивается как указатель на первый элемент (существуют исключения, подобные sizeof
оператор). * * тысяча двадцать-одна
Чтобы сделать вещи более сложными, C позволяет объявлять функции, которые выглядят так, как будто они принимают массивы, например Вы можете написать:
void foo(int bar[]);
В стандарте C есть правило, согласно которому типы параметров функции подлежат " корректировке типа ": любой параметр типа массива автоматически настраивается на соответствующий тип указателя. Итак, вышеприведенная функция на самом деле такая же как
void foo(int *bar);
Поэтому лучше забыть о передаче массивов (я бы даже рекомендовал , а не использовать синтаксис массива в параметрах функции, но это подлежит обсуждению) - вы всегда передаете указатели.
С этим знанием вы можете легко составить правильные примеры для всех ваших случаев:
(1) «Нормальный» массив :
void foo(int bar[], size_t n); // with type adjustment
void foo(int *bar, size_t n); // what it really means
// call like
int a[5];
foo(a, 5);
(2) 2D массив :
2D-массив - это массив массивов, поэтому его первый элемент сам по себе является массивом -> вы бы передали указатель на массив
void foo(int bar[][10], int n); // with type adjustment, 2d array of n x 10
void foo(int (*bar)[10], int n); // what it really means
// call like:
int a[5][10];
foo(a, 5);
(3) Массив указателей :
Массив указателей часто используется в качестве альтернативы двумерному массиву - поскольку элементы являются просто указателями, они могут указывать на отдельные значения или массивы различной длины, но недостатком является то, что у вас нет целого данные как один блок в памяти, как в случае с реальным 2d-массивом. Поскольку это просто массив указателей, он очень похож на используемый «обычный» массив:
void foo(int *bar[], int n); // with type adjustment
void foo(int **bar, int n); // what it really means
// call like:
int *a[5];
foo(a, 5);
Последнее замечание: Вы часто будете читать, что массивы "распадаются как указатели" в C. Это не официальная формулировка, но очень распространенная. Если вы объединяете правило, согласно которому идентификатор массива в большинстве контекстов (например, при передаче его функции) оценивает указатель , с правилом корректировки типа для параметров функции, то в результате синтаксис массива пишется везде , но получите указатели, так что это означает «разложение».