Вот мое мнение:
Развитие языка 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-массивы являются неудачным взломом), запрещение назначения массива все еще имеет некоторый смысл: открытый массив или параметр функции массива обрабатываются как указатель без информации о размере. Компилятор не имеет информации для создания для них назначения массива, и назначение указателя было необходимо для совместимости. Введение назначения массива для объявленных массивов привело бы к ошибкам, хотя ложные назначения (это = назначение указателя = ba или поэлементное копирование?) И другие проблемы (как передать массив по значению?) Без фактического решения проблемы - просто сделать все явно с memcpy!
/* Example how array assignment void make things even weirder in C/C++,
if we don't want to break existing code.
It's actually better to leave things as they are...
*/
typedef int vec[3];
void f(vec a, vec b)
{
vec x,y;
a=b; // pointer assignment
x=y; // NEW! element-wise assignment
a=x; // pointer assignment
x=a; // NEW! element-wise assignment
}
Это не изменилось, когда в редакцию C в 1978 году было добавлено присвоение структуры (http://cm.bell -labs.com / cm / cs / who / dmr / cchanges.pdf ). Хотя записи были различных типов в C, в ранних версиях K & R их было невозможно назначить. Вы должны были копировать их по элементам с помощью memcpy, и вы могли передавать только указатели на них в качестве параметров функции. Назначение (и передача параметров) теперь просто определялось как memcpy необработанной памяти структуры, и так как это не могло сломать существующий код, оно было легко добавлено. В качестве непреднамеренного побочного эффекта это неявно вводило какое-то назначение массива, но это происходило где-то внутри структуры, так что это не могло действительно создать проблемы с использованием массивов.