Оскільки ви тільки вивчаєте 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;