Поскольку вы только изучаете 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" внутри функции - он стал константным указателем. Однако только несколько компиляторов 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;