C не был «спроектирован» как язык; вместо этого функции были добавлены по мере необходимости, с целью не разорвать предыдущий код. Такой эволюционный подход был хорошим в те времена, когда C разрабатывался, поскольку это означало, что разработчики в большинстве случаев могли воспользоваться преимуществами более ранних улучшений в языке, прежде чем все, что может потребоваться для этого, было разработано. К сожалению, способ, которым развивались манипуляции с массивами и указателями, привел к множеству правил, которые в ретроспективе неудачны.
На современном языке C существует довольно значительная система типов, и переменные имеют четко определенные типы, но вещи не всегда были такими. Объявление char arr[8]
; будет выделять 8 байтов в текущей области и сделать arr
точкой для первой из них. Компилятор не знал бы, что arr
представляет массив - он будет представлять собой указатель на символ, как и любой другой char*
. Из того, что я понимаю, если бы кто-то объявил char arr1[8], arr2[8];
, утверждение arr1 = arr2;
было бы совершенно законным, будучи несколько эквивалентным концептуально char *st1 = "foo, *st2 = "bar"; st1 = st2;
, но почти всегда представляло бы ошибку.
правило, что массивы разлагаются в указатели, вытекающие из времени, когда массивы и указатели действительно были одинаковыми. С тех пор массивы стали признаваться как особый тип, но язык должен был оставаться практически совместимым с теми днями, когда они не были. Когда правила формулируются, вопрос о том, как обрабатывать двумерные массивы, не является проблемой, потому что такого не было. Можно было бы сделать что-то вроде char foo[20]; char *bar[4]; int i; for (i=0; i<4; i++) bar[i] = foo + (i*5);
, а затем использовать bar[x][y]
так же, как теперь использовать двумерный массив, но компилятор не будет рассматривать вещи таким образом - он просто увидел bar
как указатель к указателю. Если бы кто-то хотел сделать foo [1] точкой, совершенно отличной от foo [2], можно было бы законно сделать это.
Когда два двумерных массива были добавлены в C, нет необходимости поддерживать совместимость с предыдущим кодом, который объявлял двумерные массивы, потому что их не было. Хотя было бы возможно указать, что char bar[4][5];
будет генерировать код, эквивалентный тому, что было показано с помощью foo[20]
, и в этом случае a char[][]
можно было бы использовать в качестве char**
, считалось, что так же, как присвоение переменные массива были бы ошибкой в 99% случаев, поэтому тоже было бы перераспределение строк массива, если бы это было законно. Таким образом, массивы в C распознаются как разные типы, с их собственными правилами, которые немного нечетны, но являются тем, чем они являются.