Я думаю, что что-то упускается другими ответами.
Да, p[i]
по определению эквивалентно тому *(p+i)
, что (потому что сложение коммутативно) эквивалентно тому *(i+p)
, что (опять же, по определению []
оператора) эквивалентно i[p]
.
(И в array[i]
этом случае имя массива неявно преобразуется в указатель на первый элемент массива.)
Но коммутативность сложения не так уж очевидна в этом случае.
Когда оба операнда имеют значение того же типа, или даже различных числовых типов, которые способствовали к общему типу, коммутативности имеет смысл: x + y == y + x
.
Но в данном случае речь идет именно об арифметике указателей, где один операнд является указателем, а другой - целым числом. (Целое + целое - это другая операция, а указатель + указатель - это нонсенс.)
Описание +
оператора в стандарте C ( N1570 6.5.6) гласит:
Кроме того, либо оба операнда должны иметь арифметический тип, либо один операнд должен быть указателем на полный тип объекта, а другой должен иметь целочисленный тип.
С таким же успехом можно было бы сказать:
Кроме того, либо оба операнда должны иметь арифметический тип, либо левый
операнд должен быть указателем на полный тип объекта, а правый операнд
должен иметь целочисленный тип.
в этом случае оба i + p
и i[p]
будут незаконными.
В терминах C ++ у нас действительно есть два набора перегруженных +
операторов, которые можно условно описать так:
pointer operator+(pointer p, integer i);
а также
pointer operator+(integer i, pointer p);
из которых только первое действительно необходимо.
Так почему же так?
C ++ унаследовал это определение от C, который получил его от B (коммутативность индексации массива явно упоминается в «Справочнике пользователей B» за 1972 г. ), который получил его от BCPL (руководство от 1967 г.), который вполне мог получить его даже от более ранние языки (CPL? Algol?).
Таким образом, идея о том, что индексация массива определяется с точки зрения сложения и что сложение, даже указателя и целого числа, является коммутативной, восходит на многие десятилетия к языкам-предкам Си.
Эти языки были гораздо менее типизированы, чем современные языки. В частности, различие между указателями и целыми числами часто игнорировалось. (Ранние программисты на Си иногда использовали указатели как целые числа без знака до того, как unsigned
ключевое слово было добавлено к языку.) Поэтому идея сделать добавление некоммутативной, поскольку операнды имеют разные типы, вероятно, не возникла бы у разработчиков этих языков. Если пользователь хотел добавить две «вещи», будь то эти «вещи», являются целыми числами, указателями или чем-то еще, язык не мог предотвратить это.
И на протяжении многих лет любое изменение этого правила нарушало бы существующий код (хотя стандарт ANSI C 1989 года мог бы стать хорошей возможностью).
Изменение C и / или C ++, требующее размещения указателя слева и целого числа справа, может нарушить некоторый существующий код, но при этом не будет потеря реальной выразительной силы.
Так что теперь мы имеем в виду arr[3]
и имеем в 3[arr]
виду одно и то же, хотя последняя форма никогда не должна появляться за пределами IOCCC .