Подтвердить что ты не робот

Сравнение двух указателей void с разными объектами, определенными в С++?

Вдохновленный этим ответом о динамическом нажатии на void*:

...
bool eqdc(B* b1, B *b2) {
    return dynamic_cast<void*>(b1) == dynamic_cast<void*>(b2);
}
...
int main() {
    DD *dd = new DD();
    D1 *d1 = dynamic_cast<D1*>(dd);
    D2 *d2 = dynamic_cast<D2*>(dd);
    ... eqdc(d1, d2) ...

Мне интересно, полностью ли определено поведение С++ (согласно стандарту 03 или 11) на сравнить два указателя void для (in) равенства, которые указывают на действительные, но разные объекты.

В целом, но, возможно, не так важно, сравнивает (== или !=) два значения типа void*, которые всегда определены, или требуется, чтобы они удерживали указатель на действительную область объекта/памяти?

4b9b3361

Ответ 1

C говорит:

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

С++ говорит:

Два указателя одного типа сравнивают одинаковые, если и только если оба они равны нулю, оба указывают на одну и ту же функцию или оба представляют один и тот же адрес.

Следовательно, это будет означать, что:

а)

полностью определено поведение в С++ (в соответствии со стандартом 03 или 11) для сравнения двух указателей void для (in) равенства, которые указывают на действительные, но разные объекты.

Итак, да, в C и С++. Вы можете сравнить их, и в этом случае они будут сравниваться как истинные, если они указывают на один и тот же объект. Это просто.

б)

сравнивает (== or! =) два значения типа void *, которые всегда определены, или требуется, чтобы они содержали указатель на допустимую область объекта/памяти?

Опять же, сравнение хорошо определено (стандарт говорит "если и только если", поэтому каждое сравнение двух указателей хорошо определено). Но потом...

  • С++ говорит в терминах "адрес", поэтому я считаю, что это означает, что стандарт требует, чтобы это работало "как и следовало ожидать",
  • C, однако, требует, чтобы указатели были либо нулевыми, либо указывали на объект или функцию, либо один элемент за объектом массива. Это, если мои навыки чтения не выключены, означает, что если на данной платформе у вас есть два указателя с одинаковым значением, но не указывая на действительный объект (например, несогласованный), их сравнение должно быть четко определенным и давать false.

Это удивительно!

Действительно, не работает GCC:

int main() {
    void* a = (void*)1; // misaligned, can't point to a valid object
    void* b = a;
    printf((a == b) ? "equal" : "not equal");
    return 0;
}

результат:

equal

Может быть, UB в C имеет указатель, который не является нулевым указателем и не указывает на объект, подобъект или один за последним объектом в массиве? Хм... Это была моя догадка, но тогда у нас есть это:

Целое число может быть преобразовано в любой тип указателя. За исключением случаев, оговоренных ранее результат может быть определен неверно, может не указывать на объект ссылочного типа и может быть ловушечным представлением.

Поэтому я могу только интерпретировать его, что вышеуказанная программа хорошо определена, а стандарт C ожидает, что она напечатает "не равно", в то время как GCC на самом деле не подчиняется стандарту, но дает более интуитивный результат.

Ответ 2

С++ 11, 5.10/1:

Указатели того же типа (после конверсий указателей) можно сравнить для равенства. Два указателя одного типа сравнивают одинаковые, если и только если они оба равны нулю, оба указывают на одну и ту же функцию или оба представляют один и тот же адрес

Итак, да, конкретное сравнение в порядке.

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