Почему нельзя передавать массивы как аргументы функции в C?

12

После этого комментария я попытался Google почему, но мой Google-фу не удалось.

Комментарий по ссылке:

[...] Но важно то, что массивы и указатели - это разные вещи в C.

Предполагая, что вы не используете какие-либо расширения компилятора, вы, как правило, не можете передать сам массив в функцию, но вы можете передать указатель и проиндексировать указатель, как если бы это был массив.

Вы эффективно жалуетесь, что указатели не имеют длины. Вы должны жаловаться, что массивы нельзя передавать в качестве аргументов функции или что массивы неявно уменьшаются до указателей.

Флориан Маргейн
источник
Я не уверен, что это ответ, но частью типа массива является его размер, поэтому я считаю, что вам придется определять функцию для каждого размера, который вы хотите принять.
clcto
Вы имеете в виду как указатели на функции ? Пожалуйста, уточните вопрос.
user949300
2
@ user949300 Нет, из контекста комментария все понятно; Вы не можете передать массив в функцию, потому что он становится указателем, и он хочет знать, почему это так.
Довал
@DocBrown rlemon предложил изменить для этого.
Флориан Маргейн

Ответы:

18

Мое первое предположение по этой причине было просто из-за соображений производительности и экономии памяти, а также из-за простоты реализации компилятора (особенно для компьютеров того времени, когда был изобретен C). Передача огромных массивов «по значению», похоже, оказала огромное влияние на стек, для каждого вызова функции требуется операция полного копирования массива, и, вероятно, компилятор должен быть умнее, чтобы вывести правильный код сборки (хотя последняя точка является дискуссионной) , Также было бы сложнее обрабатывать динамически размещенные массивы так же, как статически размещенные массивы (с точки зрения синтаксиса языка).

EDIT: после прочтения некоторых частей по этой ссылке , я думаю , что реальная причина (и причина , почему массивы структур рассматриваются как типы значений, в то время как единственные массивы не) является обратной совместимостью с C предшественник B . Вот цитата из Денниса Ритчи:

[...} Решение представляет собой решающий скачок в эволюционной цепочке между типизированным BCPL и типизированным C. Это исключило материализацию указателя в хранилище и вместо этого вызвало создание указателя, когда имя массива упоминается в выражении. Правило, которое сохраняется в сегодняшнем C, заключается в том, что значения типа массива преобразуются, когда они появляются в выражениях, в указатели на первый из объектов, составляющих массив.

Это изобретение позволило большинству существующих B-кодов продолжать работать, несмотря на основополагающий сдвиг в семантике языка. [..]

Док Браун
источник
5
А struct Foo { int array[N]; } может быть передано по значению. И последнее, что касается обработки динамических и статических распределений, кажется одинаково сложным (массив в самом строгом смысле всегда включает размер, объединяющие понятия для таких вещей, как индексация массива, являются указателями в сочетании с затуханием между массивами и указателями), не могли бы вы уточнить?
@delnan: Я думаю, что изложенные здесь общие принципы являются здравыми. Понятно, что если вы заключаете свой массив в структуру, вы указываете свое намерение. В общем случае вы почти всегда собираетесь передавать по ссылке.
Роберт Харви
Я также нахожу комментарий, указанный в ОП, излишне педантичным. Очевидно, вы передаете указатель, а не массив по значению. Однако в равной степени верно то, что вы фактически передаете массив по ссылке. Если возражение заключается в том, что длина не указана, достаточно легко это пропустить.
Роберт Харви
@RobertHarvey Это все еще асимметрия в системе типов: все передается по значению, за исключением типов массивов (даже если массивы, являющиеся частью структурного типа , передаются по значению), и даже использует те же самые обозначения (оба на сайте вызова и в подписи функции).
@delnan: Почему это важно, кроме того, что вы должны помнить это?
Роберт Харви
9

Миникомпьютер PDP с только 8 КБ памяти не может выделить очень большой стек. Таким образом, на такой машине нужно быть осторожным в проектировании (или развитии) языка, чтобы иметь возможность минимизировать то, что нужно поместить в стек для ожидаемого использования общих вызовов подпрограмм. C до сих пор используется для программирования встраиваемых систем с чрезвычайно ограниченным объемом памяти (несколько КБ), поэтому компромисс, как правило, хороший.

В архитектуре процессора, в которой очень мало регистров, передача любого массива по указателю, а не по значению, чаще позволяет использовать регистр в качестве оптимизации вызова подпрограммы.

hotpaw2
источник
2
У меня есть несколько плат, которые имеют 256 байтов оперативной памяти для данных и 2K EEPROM для кода. Вы не хотите делать копию массива там.
Джерри Иеремия