Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

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

#cpp


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::end()[-1]


то же, что

std::vector::back();

    


Ответы

Ответ 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]). В случае вектора компилятор может итератор заменить на указатель и наблюдается та же история. Но как только компилятор чуточку сделает по другому (а он имеет право), может получится все, что угодно.

Ответ 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.

Комментариев нет:

Отправить комментарий