Страницы

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

суббота, 7 декабря 2019 г.

C/C++ сравнение указателей на разные объекты на равенство и отношение

#cpp #c #указатели #неопределенное_поведение


Уже который день пытаюсь разобраться, можно ли сравнивать указатели, относящиеся
к разным объектам...

Проблема заключается в том, что в Стандарте эта тема обрисована крайне расплывчато.

Вот это исследование говорит, что сравнение указателей, относящихся к разным объектам,
скорее UB, чем норма:

https://habr.com/company/pvs-studio/blog/418023/

Мои эксперименты показывают аналогичные результаты. В нескольких книгах по C11/C++11
так же нашел упоминание о том, что сравнивать можно только указатели на один и тот
же массив, плюс дополнительный элемент в конце массива.

Так же меня очень запутывает хрестоматийное соглашение о перегрузке оператора присваивания
(copy/move):

T &operator=(const T &_t)
{
    if (this != &_t)// Получается, это неопределенное поведение?
    {
    }
    return *this;
}


Плюс, я в нескольких проектах (включая софт большого банка) видел сравнение указателей
на разные объекты. Например, такое сравнение очень популярно в транзакциях, чтобы всегда
блокировать (мьютексы) в одинаковой последовательности, исключая таким образом вероятность
deadlock-а.

Мой вопрос:

Что конкретно (и по отдельности) говорят Стандарты C и C++ о сравнении указателей
на разные объекты? То, что написано в общедоступных черновиках, лично я понять не смог...
    


Ответы

Ответ 1



Денис Риччи: "Если р и q указывают на элементы одного массива, то к ним можно применять операторы отношения ==, !=, <, >= и т. д. Например, отношение вида p < q истинно, если р указывает на более ранний элемент массива, чем q. Любой указатель всегда можно сравнить на равенство и неравенство с нулем. А вот для указателей, не указывающих на элементы одного массива, результат арифметических операций или сравнений не определен." Что касается этого примера: T &operator=(const T &_t) { if (this != &_t)// Получается, это неопределенное поведение? { } return *this; } здесь производится проверка, не написал ли кто-то в коде что-то типа а = а;. В этом случае this и &_t будут указывать на один и тот же объект и (this != &_t) будет false.

Ответ 2



На практике, для всех компов, на которых я писал на языке Си, можно приводить к char * (или преобразовывать в size_t) и сравнивать. Imho после приведения к char * на равенство можно сравнивать всегда (и это останется в обозримом будущем справедливо для всех архитектур).

Ответ 3



О каком именно типе "сравнения" идет речь? Сравнивать на равенство/неравенство можно любые указатели (при условии совместимости типов), независимо от того, указывают ли они в один массив или нет. В С++ не специфицируется результат сравнения на равенство двух указателей, если один из них указывает на воображаемый элемент за одним объектом, а второй указывает на другой объект. В С в такой ситуации говорится, что указатели будут равны, если указывают в одно и то же место в памяти. Упорядочивающие сравнения разрешается применять только к указателям на элементы одного массива или полям одного класса. (Правило рекурсивно распространяется на подобъекты этих объектов.) В остальных случаях: в С++ на результат сравнения не накладывается никаких требований. В С открытым текстом говорится, что поведение не определено.

Ответ 4



Если даже не обращаться к стандарту ( в стандарте все по логике...), просто рассуждать логически: Что такое указатель?... Это место в памяти на одно машинное слово, где записывается адрес какого то обьекта или нулевое значение(нулевой указатель). Как можно сравнить эти адреса(что меньше, что больше?), по каким критериям?... Только если эти адреса совпадают или не совпадают, или нулевое или нет. А если это в пределах какого то обьекта, где адреса идут один за другим (массив), то тогда можно и сравнить какой адрес раньше(меньше) другого... По моему так...

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

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