Может ли reinterpret_cast превратить недопустимое значение указателя в действительное? - PullRequest
3 голосов
/ 25 мая 2019

Рассмотрим это объединение:

union A{
  int a;
  struct{
    int b;
    } c;
  };

c и a не совместимы с макетом типов, поэтому невозможно прочитать значение b через a:

A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)

Пробная версия 1

Для случая ниже я думаю, что начиная с C ++ 17, я также получаю неопределенное поведение:

A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12;      //(2)
*p+*p;         //(3) UB

Давайтерассмотрим [basic.type] / 3 :

Каждое значение типа указателя является одним из следующих:

  • a указатель на объект или функцию (указатель указывает на объект или функцию) или
  • a указатель после конца объекта ([expr.add]),или
  • значение нулевого указателя ([conv.ptr]) для этого типа, или
  • недопустимое значение указателя .

Давайте назовем эти 4 значения указателя категориями как значение указателя жанра .

Значение указателя может переходить из вышеупомянутого жанра в другой, ностандарт на самом деле не экспоб этом. Не стесняйтесь поправлять меня, если я ошибаюсь .Поэтому я предполагаю, что в (1) значение p является указателем на значение .Затем в (2) a жизнь заканчивается, и значение p становится недопустимым значением указателя .Итак, в (3) я получаю UB, потому что я пытаюсь получить доступ к значению объекта (a) вне его времени жизни.

Trial 2

Теперь рассмотрим этот странный код:

A x;
x.a=10;
auto p = &x.a;                 //(1)
x.c.b=12;                      //(2)
p = reinterpret_cast<int*>(p); //(2')
*p+*p;                         //(3) UB?

Может ли reinterpret_cast<int*>(p) изменить жанр значения указателя с invalid pointer value на pointer to значение.

reinterpret_cast<int*>(p) определено эквивалентным static_cast<int*>(static_cast<void*>(p)), поэтому давайте рассмотримкак определяется static_cast от void* до int*, [expr.static.cast] / 13 :

Значение типа «указатель на * 1080»* »Может быть преобразовано в значение типа« указатель на cv2 T », где T - это тип объекта, а cv2 - это та же квалификация cv, что и квалификация cv или более высокая, чем cv1.Если исходное значение указателя представляет адрес A байта в памяти и A не удовлетворяет требованию выравнивания T, то результирующее значение указателя не определено.В противном случае, если исходное значение указателя указывает на объект a , и существует объект b типа T (игнорирующий квалификацию cv), который является взаимозаменяемым указателем с a , результатом является указатель на b .В противном случае значение указателя не изменяется при преобразовании.

Таким образом, в нашем случае исходная точка указателя ed на объект a.Итак, я полагаю, что reinterpret_cast не поможет, потому что a не находится в пределах своей жизни. Является ли мое чтение строгим?Может ли этот код быть четко определен?

Ответы [ 3 ]

2 голосов
/ 26 мая 2019

Тогда в (2) жизнь заканчивается, и значение p становится недопустимым значением указателя.

Неверно.Указатели только становятся недействительными, когда они указывают на память, у которой закончилась продолжительность хранения .

Указатель в этом случае становится указателем на объект за пределами его времени жизни.Объект, на который он указывает, ушел, но указатель не является «недействительным» в том смысле, как это указано в спецификации. [basic.life] тратит немало времени на объяснение того, что вы можете и не можете делать с указателями на объекты за пределами их времени жизни.

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

0 голосов
/ 28 мая 2019

Каждая версия стандартов C и C ++ на сегодняшний день была неоднозначной или противоречивой в отношении того, что можно сделать с адресами членов профсоюза. Авторы Стандарта C не хотели требовать, чтобы компиляторы допускали пессимистическую вероятность того, что функции могут быть вызваны такими конструкциями, как:

someFunction(&myUnion.member1, &myUnion.member2);

в тех случаях, когда функция вызовет значение, один член myUnion будет изменен при доступе через другой. Хотя возможность получать адреса членов профсоюза была бы довольно бесполезной, если бы код не мог делать такие вещи, как:

someFunction1(&myUnion.member1);
someFunction2(&myUnion.member2);
someFunction3(&myUnion.member1);

авторы Стандарта ожидали, что качественные реализации, предназначенные для различных целей, будут обрабатывать конструкции, для которых Неопределенное Поведение "в документированной характеристике среды", когда это будет наилучшим образом служить этим целям, и, таким образом, считали, что создание поддержки для таких конструкций быть проблемой качества реализации было бы проще, чем пытаться сформулировать точные правила, для которых должны поддерживаться шаблоны. Компилятор, который сгенерировал код для вызываемых функций во втором примере, не зная их вызывающего контекста, не сможет чередовать доступы, выполняемые этими двумя функциями, и качественный компилятор, который расширил их встроенный во время обработки приведенного выше кода, не будет замечать этого. когда каждый указатель был получен из myUnion.

Авторы Стандарта C89 не считают необходимым определять точные правила поведения указателей на члены объединения, потому что они думали, что желание авторов компиляторов создавать качественные реализации заставит их разумно обрабатывать соответствующие случаи даже без таких правил , К сожалению, некоторые авторы компиляторов были слишком ленивы, чтобы обрабатывать случаи, подобные второму примеру, приведенному выше, и вместо того, чтобы признать, что у качественных компиляторов не было никаких причин быть неспособными обрабатывать такие случаи, авторы более поздних стандартов C и C ++ склонялись над обратным придумать странно искаженные, неоднозначные и противоречивые правила, которые оправдывают такое поведение компилятора.

В результате оператор address-of следует рассматривать как значимо применимый к членам объединения только в тех случаях, когда результирующий указатель будет использоваться для доступа к отдельным байтам хранилища, либо с использованием непосредственного использования символьных типов, либо передачи таким функциям, как memcpy, которые определены таким образом. Если или до тех пор, пока не произойдет существенное обновление Стандарта или приложения, описывающего средства, с помощью которых реализации могут предлагать необязательные гарантии, выходящие за рамки требований Стандарта, было бы лучше сделать вид, что члены объединения - как битовые поля - являются значениями, которые не у него есть адреса.

0 голосов
/ 27 мая 2019

Понятие объектов в стандарте довольно абстрактно и несколько отличается от интуиции.Объект может находиться в пределах своего времени жизни или нет, а объекты, не находящиеся в пределах их времени жизни, могут иметь один и тот же адрес, поэтому союзы работают вообще: определение активного члена - это "член, который находится в пределах его времени жизниMsgstr ".

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...