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();
Все стает просто, если знать, что конструкция 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])
.
В случае вектора компилятор может итератор заменить на указатель и наблюдается та же история. Но как только компилятор чуточку сделает по другому (а он имеет право), может получится все, что угодно.
Параметр оператора []
стандартного контейнера, как вы правильно заметили, имеет беззнаковый тип. Соответственно аргумент такого оператора при вызове будет приводиться к беззнаковому типу.
Параметр встроенного оператора []
не имеет беззнакового типа и не приводится к беззнаковому типу. Встроенный оператор []
прекрасно умеет работать и с отрицательными значениями, пока вы соблюдаете правила адресной арифметики языка.
(В целях 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
.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'
Есть задачаМы можем делать следующие действия: