Создание недействительной ссылки с помощью реинтерпретации приведения

Я пытаюсь определить, вызывает ли следующий код неопределенное поведение:

#include <iostream>

class A;

void f(A& f)
{
char* x = reinterpret_cast<char*>(&f);
for (int i = 0; i < 5; ++i)
std::cout << x[i];
}

int main(int argc, char** argue)
{
A* a = reinterpret_cast<A*>(new char[5])
f(*a);
}

Я понимаю, что reinterpret_castс и из char* соответствуют, потому что стандарт разрешает совмещение с char а также unsigned char указатели (выделение мое):

Если программа попытки доступа сохраненное значение объекта через lvalue, отличное от одного из следующих типов, поведение не определено:

  • динамический тип объекта,
  • cv-квалифицированная версия динамического типа объекта,
  • тип, который является типом со знаком или без знака, соответствующим динамическому типу объекта,
  • тип, который является типом со знаком или без знака, соответствующим cv-квалифицированной версии динамического типа объекта,
  • тип агрегата или объединения, который включает в себя один из вышеупомянутых типов среди своих членов (включая, рекурсивно, член субагрегата или автономного объединения),
  • тип, который является (возможно, квалифицированным cv) типом базового класса динамического типа объекта,
  • char или же unsigned char тип.

Тем не менее, я не уверен, f(*a) вызывает неопределенное поведение, создавая A& ссылка на неверный указатель. Похоже, решающим фактором является то, что означает «попытки получить доступ» в контексте стандарта C ++.

Моя интуиция заключается в том, что это делает не составляют доступ, так как доступ потребует A быть определенным (это объявлено, но не определено в этом примере). К сожалению, я не могу найти конкретное определение «доступа» в стандарте C ++:

Есть ли f(*a) вызвать неопределенное поведение? Что представляет собой «доступ» в стандарте C ++?

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

[Редактировать] @SergeyA процитировал этот раздел стандарта. Я включил это здесь для удобства (выделение мое):

5.3.1 / 1 [expr.unary.op]

Одинарный * Оператор выполняет косвенное обращение: выражение, к которому оно применяется, должно быть указателем на тип объекта или указателем на тип функции, а результатом является lvalue, указывающее на объект или функцию, на которые указывает выражение. Если тип выражения «указатель на TТип результата «T. »[Примечание: допустимо перенаправление через указатель на неполный тип (кроме cv void). Полученное таким образом l-значение может использоваться ограниченным образом (например, для инициализации ссылки); это значение не должно быть преобразовано в предварительное значение, см. 4.1. — конец примечания]

Прослеживая ссылку на 4.1, мы находим:

4.1 / 1 [conv.lval]

Glvalue (3.10) нефункционального типа, не являющегося массивом T может быть преобразован в prvalue. Если T является неполным типом, программа, которая требует этого преобразования, плохо сформирована. Если T не тип класса, тип prvalue является cv-unqualified версией T, В противном случае тип prvalue T,

Когда преобразование lvalue-в-значение применяется к выражению e, и либо:

  • e потенциально не оценивается, или
  • оценка e результаты в оценке члена ex из множества потенциальных результатов e, а также ex называет переменную x это не Odr-используется ex (3.2)

значение, содержащееся в к указанному объекту нет доступа.

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

6

Решение

char* x = reinterpret_cast<char*>(&f); является действительным. Или, более конкретно, доступ через x разрешено — сам актерский состав всегда действителен

A* a = reinterpret_cast<A*>(new char[5]) не является действительным — или, если быть точным, доступ через a вызовет неопределенное поведение.

Причиной этого является то, что в то время как это нормально, чтобы получить доступ к объекту через char*, это не нормально для доступа к массиву символов через случайный объект. Стандарт позволяет первое, но не второе.

Или, с точки зрения непрофессионала, вы можете псевдоним type* через char*, но вы не можете псевдоним char* через type*,

РЕДАКТИРОВАТЬ

Я просто заметил, что не ответил на прямой вопрос («Что представляет собой «доступ» в стандарте C ++«). Видимо, Стандарт не определяет доступ (по крайней мере, я не смог найти формальное определение), но разыменование указателя обычно понимается как право доступа.

4

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector