Поскольку вы только изучаете C, я рекомендую вам действительно постараться сначала понять различия между массивами и указателями вместо общих вещей.
В области параметров и массивов есть несколько запутанных правил, которые должны быть ясны перед тем, как продолжить. Во-первых, то, что вы объявляете в списке параметров, обрабатывается особым образом. Существуют такие ситуации, когда вещи не имеют смысла как параметр функции в C. Это
- Функции в качестве параметров
- Массивы в качестве параметров
Массивы в качестве параметров
Второе, возможно, не сразу ясно. Но это становится ясным, если учесть, что размер измерения массива является частью типа в C (а массив, размер измерения которого не указан, имеет неполный тип). Итак, если вы создадите функцию, которая принимает массив по значению (получает копию), то она может сделать это только для одного размера! Кроме того, массивы могут стать большими, и C пытается быть максимально быстрым.
В C по этим причинам значения массива не существуют. Если вы хотите получить значение массива, вместо этого вы получите указатель на первый элемент этого массива. И в этом на самом деле уже лежит решение. Вместо того, чтобы рисовать неверный параметр массива, компилятор C преобразует тип соответствующего параметра в указатель. Запомните это, это очень важно. Параметр не будет массивом, но будет указателем на соответствующий тип элемента.
Теперь, если вы попытаетесь передать массив, то вместо этого передается указатель на первый элемент массива.
Экскурсия: Функции в качестве параметров
Для завершения, и поскольку я думаю, что это поможет вам лучше понять суть вопроса, давайте посмотрим, как обстоят дела, когда вы пытаетесь использовать функцию в качестве параметра. Действительно, во-первых, это не имеет никакого смысла. Как параметр может быть функцией? Да, мы хотим переменную в этом месте, конечно! То, что делает компилятор, когда это происходит, опять же преобразует функцию в указатель на функцию . Попытка передать функцию передаст указатель на соответствующую функцию. Итак, следующие значения одинаковы (аналогично примеру с массивом):
void f(void g(void));
void f(void (*g)(void));
Обратите внимание, что скобки вокруг *g
необходимы. В противном случае он указывал бы функцию, возвращающую void*
, вместо указателя на функцию, возвращающую void
.
Вернуться к массивам
Теперь я сказал в начале, что массивы могут иметь неполный тип - что происходит, если вы еще не указали размер. Поскольку мы уже выяснили, что параметр массива не существует, но вместо этого любой параметр массива является указателем, размер массива не имеет значения. Это означает, что компилятор переведет все следующее, и все это одно и то же:
int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);
Конечно, бессмысленно иметь возможность помещать в него любой размер, и он просто выбрасывается. По этой причине C99 придумал новое значение для этих чисел и позволяет другим вещам появляться в скобках:
// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory.
int main(int c, char *argv[static 5]);
// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);
// says the same as the previous one
int main(int c, char ** const argv);
Последние две строки говорят о том, что вы не сможете изменить «argv» внутри функции - она стала указателем const. Только немногие компиляторы C поддерживают эти функции C99. Но эти особенности дают понять, что «массив» на самом деле не один. Это указатель.
Слово предупреждения
Обратите внимание, что все, что я сказал выше, верно только в том случае, если вы получили массив в качестве параметра функции. Если вы работаете с локальными массивами, массив не будет указателем. Он будет вести себя как указатель, потому что, как объяснялось ранее, массив будет преобразован в указатель при чтении его значения. Но это не следует путать с указателями.
Один классический пример:
char c[10];
char **c = &c; // does not work.
typedef char array[10];
array *pc = &c; // *does* work.
// same without typedef. Parens needed, because [...] has
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;