Является ли следующее четко определенным:
char* charPtr = new char[42];
int* intPtr = (int*)charPtr;
charPtr++;
intPtr = (int*) charPtr;
intPtr
неправильно выровнен (по крайней мере, в одном из двух случаев). Это незаконно просто иметь это там? Это UB, использующий это на любой стадии? Как вы можете использовать это и как вы не можете?
Во-первых, конечно: указатель гарантированно выровнен в
первый случай (согласно §5.3.4 / 10 и §3.7.4.1 / 2), и может быть правильно
выравнивается в обоих случаях. (Очевидно, если sizeof(int) == 1
, но
даже если это не так, реализация не
обязательно есть требования по выравниванию.)
И чтобы было ясно: все ваши броски reinterpret_cast
,
Кроме того, это интересный вопрос, потому что, насколько
Я могу сказать, что нет никакой разницы в двух бросках, насколько
Стандарт касается. Результаты преобразования
не указано (согласно §5.2.10 / 7); ты даже не гарантирован
что превращает его обратно в char*
приведет к
первоначальная стоимость (Это явно не будет, например, на машинах
где int*
меньше чем char*
.)
На практике, конечно: стандарт требует возврата
ценность new char[N]
быть достаточно выровненным для любого значения
что может вписаться в него, так что вы гарантированно сможете сделать:
intPtr = new (charPtr) int;
Который имеет тот же эффект, что и ваш актерский состав, учитывая, что
конструктор по умолчанию для int
это неоперация. (И при условии, что
sizeof(int) <= 42
.) Так что сложно представить реализацию
в котором первая часть терпит неудачу. Вы должны быть в состоянии использовать
intPtr
как и любой другой законно полученный intPtr
, И
Идея, что преобразование его обратно в char*
каким-то образом приведет
в другом значении от оригинала char*
кажется
нелепа.
Во второй части все ставки сняты: вы определенно не можете
разыменовывать указатель (если ваша реализация не гарантирует
в противном случае), и вполне возможно, что преобразовать его обратно
в char*
приводит к чему-то другому. (Представь себе слово
адрес машины, например, где преобразование char*
для
int*
округляет вверх. Тогда обратное преобразование приведет к
char*
который был sizeof(int)
выше, чем оригинал. Или же
где всегда приводилась попытка конвертировать смещенный указатель
в нулевом указателе.)
Как правило, результат не определен (5.2.10p7), если требования к выравниванию int
больше, чем у char
, которым они обычно будут. Результатом будет действительное значение типа int *
так может быть, например, печатается как указатель с operator<<
или преобразован в intptr_t
,
Поскольку у результата есть неопределенное значение, если это не определено реализацией, поведение не определено для его косвенного выполнения и выполнения преобразования lvalue в rvalue для результирующего int
lvalue (за исключением неоцененных контекстов). Преобразование обратно в char *
не обязательно туда-обратно.
Однако, если оригинал char *
сам был результатом броска из int *
затем актерский состав int *
считается второй половиной поездки туда и обратно; в этом случае приведение определено.
В частности, в случае выше, где char *
был результатом new[]
выражение, мы гарантируем (5.3.4p10), что char *
указатель соответствующим образом выровнен для int
, пока sizeof(int) <= 42
, Поскольку new[]
выражение получает свое хранилище из функции выделения, применяется 3.7.4.1p2; void *
указатель может быть преобразован
указатель любого полного типа объекта с фундаментальным требованием выравнивания, а затем используется для доступа к объекту […] что, наряду с примечанием к 5.3.4p10, влечет за собой char *
указатель, возвращаемый new[]
выражение. В этом случае int *
указатель на неинициализированный int
объект, поэтому выполнение преобразования lvalue в rvalue для его косвенности не определено (3.8p6), но назначение его косвенности полностью определено. int
объект в хранилище выделено (3.7.4.1p2), поэтому преобразование int *
вернуться к char *
даст исходное значение за 1.8p6. Это не относится к увеличенному char *
указатель как если sizeof(int) == 1
это не адрес int
объект.