Вот мой взгляд на это:
Развитие языка C дает некоторое представление об эволюции типа массива в C:
Попробую обрисовать массив:
Предшественники C B и BCPL не имели отдельного типа массива, такого объявления как:
auto V[10] (B)
or
let V = vec 10 (BCPL)
объявит V как (нетипизированный) указатель, который инициализируется, чтобы указывать на неиспользуемую область из 10 «слов» памяти. B уже использовался *
для разыменования указателей и имел []
сокращенную нотацию, что *(V+i)
означает V[i]
, как в C / C ++ сегодня. Однако V
это не массив, это все же указатель, который должен указывать на некоторую память. Это вызвало проблемы, когда Деннис Ричи попытался расширить B с помощью типов структур. Он хотел, чтобы массивы были частью структур, как в C сегодня:
struct {
int inumber;
char name[14];
};
Но с концепцией B, BCPL массивов как указателей, это потребовало бы, чтобы name
поле содержало указатель, который должен был быть инициализирован во время выполнения в область памяти размером 14 байтов внутри структуры. Проблема инициализации / компоновки была в конечном итоге решена путем особого обращения с массивами: компилятор отслеживал расположение массивов в структурах, в стеке и т. Д., Фактически не требуя материализации указателя на данные, за исключением выражений, которые включают массивы. Эта обработка позволила почти всему B-коду продолжать работать и является источником правила «массивы преобразовываются в указатель, если вы посмотрите на них» . Это средство обеспечения совместимости, которое оказалось очень удобным, поскольку позволяло использовать массивы открытого размера и т. Д.
И вот моя догадка, почему нельзя присвоить массив: поскольку массивы были указателями в B, вы могли просто написать:
auto V[10];
V=V+5;
перебазировать "массив". Теперь это было бессмысленно, потому что основание переменной массива больше не было lvalue. Таким образом, это задание было запрещено, что помогло отловить несколько программ, которые выполняли этот перебазинг на объявленных массивах.. А потом застряло это понятие: поскольку массивы никогда не создавались для первоклассного использования системы типов C, они в основном рассматривались как особые звери, которые становятся указателями, если вы их используете. И с определенной точки зрения (которая игнорирует тот факт, что C-массивы - это неудачная попытка взлома), запрет присваивания массивов все еще имеет смысл: открытый массив или параметр функции массива обрабатываются как указатель без информации о размере. У компилятора нет информации для генерации им присвоения массива, а присвоение указателя требовалось по соображениям совместимости.
typedef int vec[3];
void f(vec a, vec b)
{
vec x,y;
a=b;
x=y;
a=x;
x=a;
}
Это не изменилось, когда версия C в 1978 году добавила назначение структур ( http://cm.bell-labs.com/cm/cs/who/dmr/cchanges.pdf ). Несмотря на то, что записи были разными типами в C, было невозможно назначить их в ранних версиях K&R C. Вам приходилось копировать их поэлементно с помощью memcpy, и вы могли передавать на них только указатели в качестве параметров функции. Присвоение (и передача параметров) теперь просто определялось как memcpy необработанной памяти структуры, и, поскольку это не могло сломать существующий код, оно было легко адаптировано. В качестве непреднамеренного побочного эффекта это неявно вводило какое-то присваивание массива, но это происходило где-то внутри структуры, поэтому это не могло действительно создать проблемы с тем, как массивы использовались.