отрицательное число в индексах

253
28 января 2018, 02:51

Oбычно в индексах массивов, контейнеров используется беззнаковое число. К тому же unsigned(-1) компилятором преобразовывается на большое положительное число Так почему же в индексах отрицательное число дает не такой эффект? Например:

 int n[] = {1, 2, 3 };
    int* pn = &n[1];
    if (n - (&n[-1]) == 1)
        std::cout << pn[-1];                 // выведет n[1 - 1] т.е. n[0]

или std::vector<int>::end()[-1] то же, что std::vector<int>::back();

Answer 1

Все стает просто, если знать, что конструкция a[b] - это всего навсего *(a+b). Поэтому, выражение

int* pn = &n[1];

преобразуется в

int* pn = &(*(n+1));
int* pn = n+1;

и потом выражение

pn[-1]

преобразуется в *(pn-1) => *(n+1-1) => *(n+0) => n[0].

Аналогично расскрывается и выражение n - (&n[-1]).

В случае вектора компилятор может итератор заменить на указатель и наблюдается та же история. Но как только компилятор чуточку сделает по другому (а он имеет право), может получится все, что угодно.

Answer 2

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

Параметр встроенного оператора [] не имеет беззнакового типа и не приводится к беззнаковому типу. Встроенный оператор [] прекрасно умеет работать и с отрицательными значениями, пока вы соблюдаете правила адресной арифметики языка.

(В целях overload resolution, встроенный оператор [] рассматривается как имеющий параметр типа std::ptrdiff_t, который является знаковым)

Правила адресной арифметики говорят, что адресная арифметика в С и С++ поддерживается только среди элементов одного массива. Соответственно отрицательные индексы в [] могут быть использованы только в том случае, если доступ осуществляется относительно указателя, указывающего куда-то "в середину" существующего массива.

В вашем примере кода выражение pn[-1] удовлетворяет этим правилам, а в выражении n[-1] это правило нарушено. Выражение n[-1] порождает неопределенное поведение. Код формально неработоспособен.

Выражение вида some_vector.end()[-1] формально некорректно. К итератору контейнера в общем случае вообще не применим оператор []. Скомпилироваться такое выражение может только случайно за счет того, что в некоторой реализации итераторы std::vector оказались реализованы как обычные "голые" указатели.

Попыткой создания корректной формы такого выражения может быть (&*some_vector.end())[-1], т.е. преобразование итератора в "голый" указатель, и последующее применение к нему оператора []. Но в таком случае мы применяем оператор * к итератору some_vector.end(), что порождает неопределенное поведение.

Так что пытаться индексироваться "назад" от end() итератора при помощи опратора [] - плохая идея. Пользуйтесь для этого std::next/std::prev.

READ ALSO
кодировка строк в Java

кодировка строк в Java

Учу яваПри написание потребовалась перекодировать полученную строку

229
Unable to merge dex

Unable to merge dex

Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'

387
Как в андроид сделать кнопку-список (без ActionBar)?

Как в андроид сделать кнопку-список (без ActionBar)?

Коротко о проблеме: Есть кнопка

172
Вывести наименьшее Y, при котором возможно получить Z [требует правки]

Вывести наименьшее Y, при котором возможно получить Z [требует правки]

Есть задачаМы можем делать следующие действия:

186