Чому масиви не можуть бути передані як аргументи функції в C?


12

Після цього коментаря я намагався google чому, але мій google-fu не вдався.

Коментар за посиланням:

[...] Але важливим є те, що масиви та покажчики - це різні речі в C.

Якщо припустити, що ви не використовуєте жодних розширень компілятора, ви не можете передавати масив самій функції, але ви можете передавати вказівник та індексувати покажчик так, ніби це масив.

Ви фактично нарікаєте, що покажчики не мають доданої довжини. Вам слід скаржитися, що масиви не можуть бути передані як аргументи функції, або що масиви деградують на покажчики неявно.


Я не впевнений, що це відповідь, але частиною типу масиву є його розмір, тому я вважаю, що вам доведеться визначити функцію для кожного розміру, який ви бажаєте прийняти.
clcto

Ви маєте на увазі функціональних покажчиків? Проясніть, будь ласка, питання.
user949300

2
@ user949300 Ні, з контексту коментаря це досить зрозуміло; ви не можете передати масив функції, тому що він стає вказівником, і він хоче знати, чому це так.
Doval

@DocBrown rlemon запропонував зміни для цього.
Флоріан Маргаїн

Відповіді:


18

Моя перша здогадка про причину була просто через причини продуктивності та збереження пам'яті, а також для простоти реалізації компілятора (особливо для типу комп'ютерів у той час, коли був винайдений C). Передача величезних масивів "за значенням", здається, має величезний вплив на стек, для кожного виклику функції потрібна повна операція копіювання масиву, і, ймовірно, компілятор повинен бути розумнішим, щоб вивести правильний код складання (хоча останній пункт є дискусійним) . Було б також складніше трактувати динамічно розподілені масиви так само, як і статично розподілені масиви (з точки зору синтаксису мови).

EDIT: після прочитання деяких частин за цим посиланням , я думаю , що реальна причина (і причина , чому масиви структур розглядаються як типи значень, в той час як єдині масиви ні) є зворотною сумісністю з C попередник B . Ось цитата від Денніса Річі:

[...} Рішення стало вирішальним стрибком еволюційного ланцюга між безтиповим BCPL та типізованим C. Це усунуло матеріалізацію вказівника на сховищі, а натомість спричинило створення покажчика, коли ім'я масиву згадується у виразі. Правило, яке збереглося в сьогоднішньому С, полягає в тому, що значення типу масиву перетворюються, коли вони з'являються в виразах, в покажчики до першого з об'єктів, що складають масив.

Цей винахід дав можливість більшості існуючих код B продовжувати працювати, незважаючи на зміну основної мови в семантиці мови. [..]


5
А struct Foo { int array[N]; } може передаватися за значенням. І останній біт щодо обробки динамічних та статичних виділень - це те саме, що здається рибним (масив у найсуворішому сенсі завжди включає розмір, об'єднуючі поняття для таких речей, як індексація масиву - покажчики в поєднанні з розпадом масиву до вказівника), чи могли б ви детальніше?

@delnan: Я думаю, загальні принципи, викладені тут, є здоровими. Зрозуміло, що якщо ви загортаєте масив у структуру, ви вказуєте свій намір. У загальному випадку ви майже завжди збираєтеся пройти посилання.
Роберт Харві

Я також вважаю, що коментар, на який посилається в ОП, є зайвим педантичним. Ясно, що ви передаєте значення, а не масив за значенням. Однак однаково правдою є те, що ви ефективно передаєте масив за посиланням. Якщо заперечення полягає в тому, що довжини не додається, досить просто пропустити це.
Роберт Харві

@RobertHarvey Це все-таки асиметрія в системі типів: все передається за значенням, за винятком типів масивів (навіть якщо масиви, що входять до типу структури , передаються за значенням), і він навіть використовує абсолютно однакові позначення (обидва на сайті виклику і в підписі функції).

@delnan: Чому це актуально, окрім того, як ви повинні його пам’ятати?
Роберт Харві

9

Мінікомп'ютер PDP із лише 8 кБ пам'яті не може виділити дуже великий стек. Отже, на такій машині потрібно бути обережним у мовному дизайні (або еволюції), щоб мати змогу мінімізувати те, що потрібно продовжувати на стеці для очікуваного загального використання виклику підпрограми. C до цих пір використовується для програмування вбудованих систем з надзвичайно обмеженою пам'яттю (кілька кБ), тому компроміс зазвичай є хорошим.

У архітектурі процесора, яка має дуже мало регістрів, передача будь-якого масиву за вказівником, а не за значенням, частіше дозволяє реєстру використовуватись як оптимізація виклику підпрограми.


2
У мене є кілька плат, які мають 256 байт оперативної пам’яті для даних і 2K EEPROM для коду. Ви не хочете робити там копію масиву.
Джеррі Єремія
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.