Сравнивает ли два указателя void с разными объектами, определенными в C ++? - PullRequest
13 голосов
/ 22 ноября 2011

Вдохновленный этим ответом о динамическом приведении к 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) ...

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

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

Ответы [ 2 ]

11 голосов
/ 22 ноября 2011

C говорит:

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

C ++ говорит:

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

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

a)

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

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

b)

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

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

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

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

На самом деле это не так, как работает 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 на самом деле не подчиняется стандарту, но дает более интуитивный результат.

3 голосов
/ 22 ноября 2011

С ++ 11, 5.10 / 1:

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

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

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

...