Поддерживают ли указатели «индексацию стиля массива»? - PullRequest
5 голосов
/ 18 апреля 2019

(Ответы на вопросы с самоответом - этот вопрос постоянно всплывает)

Я предполагаю, что читатель знает о том, как работает арифметика указателей.

int arr[3] = {1,2,3};
int* ptr = arr;
...
*(ptr + i) = value;

УчителяВ книгах / C постоянно говорится, что я не должен использовать *(ptr + i), как в примере выше, потому что «указатели поддерживают индексацию в стиле массива», и вместо этого я должен использовать ptr[i] = value;.Никаких аргументов - намного проще для чтения.

Но, просматривая стандарт C, я не нахожу ничего, что называется «индексация в стиле массива».Фактически, оператор [] не ожидает, что любой операнд будет массивом, а вместо этого указатель или целое число!

6.5.2.1 подписка на массив

Ограничения

Одно из выражений должно иметь указатель типа '' для завершения объекта тип '', другое выражение должно иметь целочисленный тип, а результатимеет тип '' type ''.

Почему оператор подписки array не ожидает массива?Стандарт не так?Моя книга учителя / C перепутана?

1 Ответ

6 голосов
/ 18 апреля 2019

Вы действительно должны использовать ptr[i] над *(ptr + i) для удобства чтения.Но кроме этого, оператор [], строго говоря, фактически никогда не используется с операндом массива.

Массивы, когда используются в выражении, всегда "распадаются" в указатель на первый элемент (снекоторые исключения).C17 6.3.2.1/3, выделено:

За исключением случаев, когда это операнд оператора sizeof или унарный оператор &, или строковый литерал, используемый для инициализации массива, выражение с типом '' массив типа '' преобразуется в выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и не является lvalue.

Это означает, что всякий раз, когда вы вводите arr[i], операнд arr заменяется указателем на первый элемент в этом массиве.Это неофициально называется «распадом массива».Больше информации здесь: Что такое распадающийся массив?

Поэтому, когда вы используете оператор [], вы используете его для указателя.Всегда.

Стандарт C говорит, что этот оператор гарантированно эквивалентен арифметике указателя (C17 6.5.2.1/2):

Определение оператора индекса []:что E1[E2] идентично (*((E1)+(E2))).

Так что всякий раз, когда мы набираем arr[i], он фактически заменяется на *(arr+i).Где arr по-прежнему является указателем на первый элемент.

И вот почему приведенное вами описание говорит вам, что операнд либо может быть указателем, а другой - целым числом.Потому что, очевидно, не имеет значения, если мы введем *(arr+i) или *(i+arr) - это эквивалентный код.

Что, в свою очередь, позволяет нам написать запутанный код "шутки", такой как i[arr], который на самом деле является допустимым C и полностью эквивалентен arr[i].Но не пишите такой код в реальных приложениях.

...