Те, що ви говорите у своєму дописі, абсолютно коректно. Я б сказав, що кожен розробник C приходить до абсолютно одного і того ж відкриття і абсолютно такого ж висновку, коли (якщо) вони досягають певного рівня володіння мовою C.
Коли специфіка області вашої програми вимагає масиву певного фіксованого розміру (розмір масиву - константа часу компіляції), єдиний правильний спосіб передавати такий масив функції - це за допомогою параметра вказівника на масив
void foo(char (*p)[10]);
(мовою C ++ це також робиться з посиланнями
void foo(char (&p)[10]);
).
Це дозволить перевірити тип мови на рівні мови, що переконається, що масив точно правильного розміру подається як аргумент. Насправді, у багатьох випадках люди використовують цю техніку неявно, навіть не усвідомлюючи її, приховуючи тип масиву за назвою typedef
typedef int Vector3d[3];
void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);
Додатково зауважте, що вищевказаний код є інваріантним щодо Vector3d
типу, який є масивом або a struct
. Ви можете Vector3d
будь-коли переключити визначення масиву з масиву на a struct
і назад, і вам не доведеться змінювати декларацію функції. У будь-якому випадку функції отримають сукупний об’єкт "за посиланням" (є винятки з цього, але в контексті цього обговорення це правда).
Однак ви не побачите цей метод передачі масиву, який використовується явно занадто часто, просто через те, що занадто багато людей плутають доволі зведений синтаксис і просто недостатньо комфортні з такими особливостями мови С, щоб правильно їх використовувати. З цієї причини в середньому реальному житті передача масиву як вказівника на його перший елемент є більш популярним підходом. Це просто виглядає "простіше".
Але насправді використання вказівника на перший елемент для проходження масиву - це дуже ніша техніка, трюк, який служить дуже конкретній цілі: його єдиною метою є полегшення передачі масивів різної величини (тобто розміру часу виконання) . Якщо вам дійсно потрібно мати можливість обробляти масиви розміру часу виконання, то правильний спосіб передавати такий масив - це вказівник на його перший елемент із розміром конкретного поданого додатковим параметром
void foo(char p[], unsigned plen);
Насправді, у багатьох випадках дуже корисно вміти обробляти масиви розміру часу, що також сприяє популярності методу. Багато розробників C просто ніколи не стикаються (або ніколи не визнають) необхідності обробляти масив фіксованого розміру, залишаючи при цьому поза увагою правильну техніку фіксованого розміру.
Тим не менш, якщо розмір масиву виправлений, передаючи його як вказівник на елемент
void foo(char p[])
є головною помилкою на рівні техніки, яка, на жаль, є досить поширеною в наші дні. Техніка вказівника на масив - набагато кращий підхід у таких випадках.
Ще одна причина, яка може перешкоджати прийняттю методики проходження масиву фіксованого розміру - це домінування наївного підходу до введення динамічно розподілених масивів. Наприклад, якщо програма вимагає встановлення фіксованих масивів типу char[10]
(як у вашому прикладі), середній розробник буде мати malloc
такі масиви, як
char *p = malloc(10 * sizeof *p);
Цей масив не може бути переданий функції, оголошеній як
void foo(char (*p)[10]);
що збиває з пантелику середнього розробника і змушує їх відмовитися від декларації параметра фіксованого розміру, не даючи йому більше думати. Насправді ж корінь проблеми полягає в наївному malloc
підході. malloc
Формат , показаний вище , повинен бути зарезервований для масивів розміру часу виконання. Якщо тип масиву має розмір часу компіляції, кращий спосіб malloc
виглядатиме наступним чином
char (*p)[10] = malloc(sizeof *p);
Це, звичайно, можна легко передати вищезазначеному foo
foo(p);
і компілятор проведе відповідну перевірку типу. Але знову ж таки, це надто заплутано для непідготовленого розробника C, через що ви не будете бачити його занадто часто у "типовому" середньоденному коді.
10
його можна замінити будь-якою змінною за обсягом