Всегда ли (void *) ptr == ptr верно? - PullRequest
3 голосов
/ 09 июня 2019

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

6.3.2.3

1008 * Pointers *

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

...

4 Преобразование нулевого указателя в другой тип указателя дает нулевой указатель этого типа. Любые два нулевых указателя должны сравниваться равны.

Теперь говорится только, что

(originaltype*)((void*)ptr) == ptr

всегда должно быть правдой, но как насчет

(void*) ptr == ptr

Точно не указано, является ли это истиной или ложью. Или я неправильно истолковал 1 абзац?

Ответы [ 2 ]

4 голосов
/ 09 июня 2019

C 2018 6.5.9 обсуждается ==.Параграф 2 определяет ограничения, а (void *) ptr == ptr удовлетворяет ограничениям, потому что один из параметров - «один операнд является указателем на тип объекта, а другой - указателем на квалифицированную или неквалифицированную версию void».Затем в параграфе 5 говорится: «… Если один операнд является указателем на тип объекта, а другой - указателем на квалифицированную или неквалифицированную версию void, первый преобразуется в тип последнего».

Таким образомв (void *) ptr == ptr правый операнд преобразуется в (void *), поэтому выражение эквивалентно (void *) ptr == (void *) ptr, и мы можем ожидать, что оно оценивается как true.

Строго говоря, предложение о преобразовании указателя,6.3.2.3, говорит нам только, что результат преобразования (void *) ptr обратно в исходный тип будет сравниваться равным ptr.Он ничего не говорит нам о значении (void *) ptr, и поэтому, рассматривая только этот пункт, возможно, что два разных экземпляра (void *) ptr будут давать разные результаты, если они содержат достаточно информации, чтобы произвести что-то, чтобудет сравниваться равным исходному ptr при обратном преобразовании.

Возвращаясь к 6.5.9, параграф 6 говорит нам:

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

Теперь, конечно, мы ожидаем (void *) ptr == (void *) ptrчтобы быть правдой, по крайней мере, иногда.Как это возможно?(void *) ptr не является нулевым указателем (при условии, что ptr не было), и при этом мы не ожидаем, что этот случай покрывается концом одного массива и началом другого случая.Таким образом, мы ожидаем, что, когда (void *) ptr == (void *) ptr оценивается как true, это должно быть потому, что он находится в случае «указатели на один и тот же объект» или «указатели на один после последнего элемента того же самого случая объекта массива».Это кажется единственным разумным способом интерпретации стандарта.Если это так, то этот случай (в зависимости от того, который применяется иногда) должен применяться постоянно, и «если и только если» говорит нам, что (void *) ptr == (void *) ptr всегда верно.

1 голос
/ 09 июня 2019

* Если ptr является указателем на тип объекта, то (void *) ptr == ptr эквивалентно (void *) ptr == (void *) ptr. ptr справа неявно преобразуется в void *. (Если это указатель на квалифицированный тип const или volatile, эти квалификаторы теряются при неявном преобразовании.)

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

Если ptr является указателем на функцию, то (void *) ptr == ptr требует диагностики; но ясно, что речь идет о типах объектов.

...