Ось мій погляд на це:
Розвиток мови C пропонує деяке розуміння еволюції типу масиву на мові C:
Я спробую окреслити масив:
Попередники C та BCPL не мали чіткого типу масиву, декларація типу:
auto V[10] (B)
or
let V = vec 10 (BCPL)
оголосить V як (нетипізований) покажчик, який ініціалізується, щоб вказати на невикористану область з 10 "слів" пам'яті. B вже використовувався *
для розмежування покажчиків і мав []
коротке позначення рук, *(V+i)
мається на увазі V[i]
, як і в C / C ++ сьогодні. Однак V
це не масив, це все одно покажчик, який повинен вказувати на деяку пам’ять. Це спричинило проблеми, коли Денніс Річі спробував розширити B за допомогою типів struct. Він хотів, щоб масиви були частиною конструкцій, як у C сьогодні:
struct {
int inumber;
char name[14];
};
Але з концепцією масивів B, BCPL як покажчиків, це зажадало б, щоб name
поле містило покажчик, який повинен був ініціалізуватися під час виконання до області пам'яті 14 байтів у структурі. Проблема ініціалізації / компонування врешті-решт була вирішена шляхом надання масивам спеціальної обробки: компілятор відстежував би розташування масивів у структурах, у стеку тощо, фактично не вимагаючи вказівника на дані для матеріалізації, за винятком виразів, що включають масиви. Ця обробка дозволила продовжувати працювати майже усьому коду B і є джерелом правила "перетворення масивів у покажчик, якщо ви дивитесь на них" . Це хакер сумісності, який виявився дуже зручним, оскільки дозволив масиви відкритого розміру тощо.
І ось моє здогадування, чому масив неможливо призначити: Оскільки масиви були вказівниками на B, ви можете просто написати:
auto V[10];
V=V+5;
перебазувати "масив". Це тепер було безглуздо, оскільки основа змінної масиву вже не була значенням. Тож це призначення було заборонено, що допомогло вловити кілька програм, які здійснили це перебазування на оголошені масиви. І тоді це поняття застрягло: Оскільки масиви ніколи не були розроблені для того, щоб вказувати їх на першокласні системи типу С, їх переважно розглядали як особливих звірів, які стають покажчиком, якщо ви їх використовуєте. І з певної точки зору (яка ігнорує, що 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 необробленої пам'яті структури, і оскільки це не могло зламати існуючий код, воно було легко призначене. Як непередбачуваний побічний ефект, це неявно вводило якесь призначення масиву, але це траплялося десь усередині структури, тому це насправді не могло спричинити проблем із способом використання масивів.