Стандартне використання масиву в C із природним розкладом типу від масиву до ptr
@Bo Перссон правильно стверджує у своїй чудовій відповіді тут :
При передачі масиву як параметра, це
void arraytest(int a[])
означає абсолютно те саме, що і
void arraytest(int *a)
Однак дозвольте також додати, що дві наведені вище форми також:
означати точно так само, як
void arraytest(int a[0])
що означає абсолютно те саме, що і
void arraytest(int a[1])
що означає абсолютно те саме, що і
void arraytest(int a[2])
що означає абсолютно те саме, що і
void arraytest(int a[1000])
тощо
У кожному з наведених вище прикладів масиву тип вхідного параметра перетворюється наint *
і може бути викликаний без попереджень та помилок, навіть з -Wall -Wextra -Werror
увімкненими параметрами збірки (див. Моє репо тут для деталей цих 3 варіантів збірки), наприклад це:
int array1[2];
int * array2 = array1;
arraytest(array1);
arraytest(array2);
Справді, «розмір» значення ( [0]
, [1]
, [2]
, [1000]
і т.д.) усередині параметра масиву тут, по- видимому тільки для цілей естетичного / самодокументірованія, і може бути будь-яким позитивним цілим числом (size_t
типу я думаю) ви хочете!
На практиці, однак, ви повинні використовувати його, щоб вказати мінімальний розмір масиву, який ви очікуєте отримати функцією, так що під час написання коду вам буде легко відстежувати та перевіряти. Стандарт MISRA-C-2012 (тут придбайте / завантажте PDF із стандартом 236-сторінкової версії 2012 року за 15,00 фунтів стерлінгів ) доходить до ствердження (курсив додано):
Правило 17.5. Аргумент функції, що відповідає параметру, який оголошений типом масиву, повинен мати відповідну кількість елементів.
...
Якщо параметр оголошено як масив із заданим розміром, відповідний аргумент у кожному виклику функції повинен вказувати на об'єкт, що має принаймні стільки елементів, скільки масив.
...
Використання декларатора масиву для параметра функції задає інтерфейс функції більш чітко, ніж використання вказівника. Мінімальна кількість елементів, що очікується функцією, чітко зазначена, тоді як це неможливо за допомогою покажчика.
Іншими словами, вони рекомендують використовувати формат явного розміру, хоча технічно стандарт С не застосовує його - він принаймні допомагає пояснити вам як розробнику та іншим, хто використовує код, який масив розміру очікує функція вам пройти.
Примусове забезпечення типу масивів у C
Як зазначає @Winger Sendon у коментарі під моєю відповіддю, ми можемо змусити C обробляти тип масиву різним залежно від розміру масиву. !
По-перше, ви повинні визнати, що в моєму прикладі трохи вище, використовуючи int array1[2];
подібне: arraytest(array1);
призводить array1
до автоматичного розпаду в файл int *
. Втім, якщо замість цього взяти адресу array1
і зателефонувати arraytest(&array1)
, ви отримаєте зовсім іншу поведінку! Тепер він НЕ розпадається на int *
! Натомість тип &array1
is int (*)[2]
, що означає "вказівник на масив розміром 2 з int" , або "вказівник на масив розміром 2 типу int" . Отже, ви можете FORCE C перевірити безпеку типу на масиві, наприклад:
void arraytest(int (*a)[2])
{
}
Цей синтаксис важко читати, але подібний до синтаксису покажчика функції . Інтернет-інструмент, cdecl , повідомляє нам, що int (*a)[2]
означає: "оголосити a як вказівник на масив 2 з int" (вказівник на масив 2 int
s). НЕ плутайте це з версією з дужками OUT:, int * a[2]
що означає: "оголосити масив 2 покажчика на int" (масив з 2 покажчиків на int
).
Тепер ця функція ПОТРІБНА викликати її за допомогою оператора адреси ( &
) таким чином, використовуючи як вхідний параметр ВКАЗНИК НА МАСИВ ПРАВИЛЬНОГО РОЗМІРУ !:
int array1[2];
arraytest(&array1);
Однак це дасть попередження:
int array1[2];
arraytest(array1);
Ви можете протестувати цей код тут .
Щоб змусити компілятор C перетворити це попередження на помилку, так що ви ПОВИННІ завжди телефонувати, arraytest(&array1);
використовуючи лише вхідний масив правильного розміру та типу ( int array1[2];
у цьому випадку), додайте -Werror
до параметрів збірки. Якщо ви запускаєте тестовий код вище на сайті onlinegdb.com, зробіть це, клацнувши піктограму шестірні у верхньому правому куті та клацнувши на «Додаткові прапорці компілятора», щоб ввести цю опцію. Тепер це попередження:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
перетвориться на цю помилку збірки:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1);
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
Зверніть увагу, що ви також можете створити покажчики "безпечного типу" на масиви заданого розміру, наприклад:
int array[2];
int (*array_p)[2] = &array;
... але я не обов'язково рекомендую це, оскільки це мені нагадує багато витівок на C ++, що використовуються для забезпечення безпеки типів скрізь, при надзвичайно високій вартості мовної синтаксичної складності, багатослівності та складності архітектури коду, і що мені не подобається і вже багато разів оцінювали (наприклад: див. "Мої думки на C ++" тут ).
Додаткові тести та експерименти див. Також за посиланням трохи нижче.
Список літератури
Див. Посилання вище. Також:
- Моє експериментування з кодом в Інтернеті: https://onlinegdb.com/B1RsrBDFD