Страницы

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

воскресенье, 9 февраля 2020 г.

Безопасное сравнение указателей

#cpp


Безопасен ли данный код.

#include 

int main(int argc, char* argv[] )
{
    constexpr auto SIZE = 3;
    int* a1 = new int[3];

    a1[0] = 1;
    a1[1] = 2;
    a1[2] = 3;

    int* first = a1;
    int* last = a1 + SIZE;

    for ( ; first != last; first++)
        std::cout << *first;

    return 0;
}


В конце последней итерации first вне диапазона, можно ли так сравнивать указатели.
Что будет если first будет равен максимальному size_t.
    


Ответы

Ответ 1



Сравнивать указатели можно любые (лишь бы сравнимые :)). Разыменовывать нельзя - но вы этого и не делаете :) Заметим в скобках, что ваш код - по сути обычная работа с итераторами, именно так, как она обычно реализуется - пока итератор не сравняется с итератором, указывающим за конец контейнера.

Ответ 2



Относительно указателя на "элемент", следующий непосредственно за последним элементом массива стандарт языка говорит следующее: n4659 6.9.2/3: [...] For purposes of pointer arithmetic (8.7) and comparison (8.9, 8.10), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical element x[n]. [...] То есть, для целей арифметики указателей и сравнения указателей, указатель, указывающий на элемент, следующий непосредственно за последним элементом массива x из n элементов, считается эквивалентным указателю на гипотетический элемент x[n]. Более того, если некоторый объект не является элементом массива, то для целей арифметики указателей и сравнения указателей, этот объект считается принадлежащим массиву из одного элемента. n4659 8.3.1/3: [...] For purposes of pointer arithmetic (8.7) and comparison (8.9, 8.10), an object that is not an array element whose address is taken in this way is considered to belong to an array with one element of type T. [...] В этом же пункте приведён такой пример кода: int a; int* p1 = &a; int* p2 = p1 + 1; // defined behavior bool b = p2 > p1; // defined behavior, with value true P.S. "Что будет если first будет равен максимальному size_t." Также стоит заметить, что стандарт языка не гарантирует, что указатели имеют такое же внутреннее представление как и тип size_t, стандарт также не гарантирует, что sizeof(int *) == sizeof(size_t), и наконец, стандарт даже не гарантирует, что в реализации будут присутствовать целочисленные типы, способные вместить в себя указатель. Однако, если таковые целочисленные типы в реализации всё же присутствуют, то в заголовочном файле должны быть определены целочисленные типы std::intptr_t и std::uintptr_t, способные хранить указатели на объектные типы. P.P.S. Предположим, что тип int * имеет в точности такое же внутреннее представление как и тип size_t. Насколько я понял вопрос, вам интересно, а возможна ли на практике ситуация, когда адрес последнего байта некоторого объекта (в вашем примере массива a1) равен значению SIZE_MAX. И если возможна, то чему тогда равен указатель, указывающий на элемент следующий непосредственно за последним элементом массива a1, и корректно ли будут происходить операции сравнения с таким указателем. С точки зрения стандарта языка такой проблемы нет. Указатель на элемент, следующий непосредственно за последним элементом массива всегда существует и с ним работает как арифметика указателей, так и операции сравнения. Однако, каким именно образом реальными компиляторами реализуется это требование стандарта языка, я не знаю.

Ответ 3



В реальной жизни (с учетом new) этот код безопасен. Однако, если рассмотреть (почти) гипотетический пример, когда ваш массив это вся доступная память, размер которой совпадает с максимальным значением uintptr_t (если представить что, мы работаем в 16-bit архитектуре, то в этом нет ничего необычного) и first в начале программы указывает на ее начало (т.е. попросту first = 0), то он работать не будет, поскольку last также окажется равен 0 и проверка в for сразу же прекратит выполнение цикла. В подобном случае (когда мы хотим просмотреть всю память) придется написать что-то вроде: do { ... } while (++first != last);

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

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