Итератор произвольного доступа с вычитанием или уменьшением, указывающий на начало - PullRequest
1 голос
/ 16 декабря 2011

Рассмотрим следующий фрагмент кода

void foo( bool forwad )
{
    vector<MyObject>::iterator it, end_it;
    int dir;

    it = some_global_vector.begin() + some_position;
    if( forward )
    {
        dir = 1;
        it += 1;
        end_it = some_global_vector.end();

    }
    else
    {
        dir = -1;
        it -= 1;
        end_it = some_global_vector.begin()-1;
    }

    while( it != end_it )
    {
       if( do_domething() )
         break;

       it += dir;
    }
}

Как вы можете видеть, есть некоторые сомнения, когда forward == false, потому что есть вычитание из begin() и итератор it может быть вычтен, когда он указывает на begin(). Я нигде не могу найти, если это нормально, пока я не разыграю этот плохой указывающий итератор).

EDIT

Я прочитал стандарт ISO C ++ и у меня есть некоторые выводы. Нет никаких обещаний, что vector::begin() не сможет внутренне указать на память по адресу 0, и я подумал, что это конец, но все контейнеры зависят от стандартного локатора. Этот локатор зависит от оператора new. А также нет информации о том, что new никогда не вернет 0. Но стандартный локатор зависит также от оператора delete, и этот оператор может ничего не делать, если вы передадите 0. Таким образом, по этому факту new не может вернуть 0, потому что не будет никакого способа удалить этот указатель, и при этом непустой vector не может вернуть begin(), который указывает на 0.

Вывод:

Если выше - правый убывающий интегратор, который указывает на vector::begin(), должен быть безопасным, поскольку внутренняя память vector является непрерывной.

Я прав?

ОТЛИЧНЫЙ ОТВЕТ

Даже если он работает сейчас и будет работать в будущем, это ненадлежащее поведение в соответствии со стандартом. Если вы делаете это, вы делаете это на свой страх и риск. См. Этот simmilar вопрос для получения дополнительной информации.

1 Ответ

6 голосов
/ 16 декабря 2011

Вы не можете уменьшить значение переданного итератора начала или вычислить begin() - 1.

В то время как реализация должна иметь позицию для одного переданного последнего элемента, она не обязана иметь доступное адресное пространство перед началом. Так что begin() - 1 может быть неверным адресом (и определенно не действительным итератором).


На вопрос № 2:

Даже если if (p == 0) проверяет, является ли указатель нулевым, это не означает, что нулевой указатель должен быть представлен всеми нулевыми битами. Это также могут быть все биты 1 или что-то еще. Магия компилятора заставит тест работать в любом случае.

Еще один пример неверного адреса: когда вы освобождаете большой блок памяти, менеджер кучи просто может также удалить соответствующее виртуальное адресное пространство из вашего процесса.

Другой блок памяти, начинающийся сразу после освобождения места, может иметь адрес, скажем, 0x10000, где адрес 0x10000 - 1 больше не существует. Известно, что некоторое оборудование, которое использует выделенные регистры адресов для указателей, перехватывает при загрузке недопустимого указателя. Просто может обнаружить, что 0x10000 - 1 больше не отображается в ОЗУ, и прервать вашу программу. Стандарт написан, чтобы позволить это, потому что такое оборудование существует.

Мы не говорим, что это обычно происходит в обычных настольных операционных системах, именно то, что может происходить в соответствии со стандартом языка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...