Изначально был вопрос про "зачем вынимать байты". Интерпретировать одну и ту же область памяти как значения двух разных сложных типов нужно и правда сильно реже.
Не совсем. Изначально, я привел ссылку на статью с куском из стандартных рекомендаций. Там говорилось, что не надо использовать union'ы таким способом как это предлагал guryev. Там был пример, где получают кусок char(1 байт) от int(32бита обычно, 4-байта), т.е. речь шла именно получить кусок от одной переменной. А ещё заметил, что непонятно, что и как реально хотят от ТС. Я не хотел вступать в дискуссии. Дальше мне пришлось
пояснить, что процитированный мной кусок из стандартных рекомендаций. И ещё, что там ясно сказано, что доступ через reinterpret_cast к объектам с другим типом , есть определенное поведение("Accessing the result of an reinterpret_cast to a different type from the objects declared type is defined behavior"), не ub, в отличии от через union'ы. А также заметил, что авторы удивляются(is discouraged), что такое обескураживает, даже через reinterpret_cast, хотя это не UB, а я так вообще не понимаю зачем такое может понадобится.
Собирать числа из байтов сдвигами долго. А более сложные структуры - и вообще невозможно.
Собирать числа из байтов не надо. А более сложные структуры тем более. Для большинства задач есть стандартные типы. Минимальный в байт это char. Если чего нет, вы определяете свою структуру/класс и с ним работаете, помещаете её в подходящие контейнеры или куда хотите. Если вдруг, байт слишком крупный, надо работать с каким timestamp файловой системы, вынуть сдвигами, поместить в свою структуру и работать с ней.
Если же скомпилировать код с -O2, то компилятор решает, что запись в int32_t не может повлиять на int64_t, поэтому можно не перечитывать значение *i64, а просто сразу вернуть то, что туда только что записали. И в результате функция возвращает 1.
Да и правда, и с -O3 тоже, только что проверил, что вы сразу не сказали. Я думал удивляет, как раз нормальный вывод.
Доступ к данным одного типа через несовместимый с ними указатель - это UB, независимо от способа получения этого указателя.
Может быть, я не спорю(хотя и сомневаюсь, пока не увижу, или не найду это в стандарте). Но, что к char можно и из char точно можно, и это не UB , вопросов нет?
Вобщем, поправил изначальный свой пример вот так:
#include <iostream>
using namespace std;
int main()
{
uint64_t tt=0x600000005 ;
char* tt1 = reinterpret_cast<char*>(&tt);
uint32_t tt32_1 = *(reinterpret_cast<uint32_t*>(&(tt1[0]))) ;
uint32_t tt32_2 = *(reinterpret_cast<uint32_t*>(&(tt1[4]))) ;
std::cout << tt32_1 << std::endl;
std::cout << tt32_2 << std::endl;
return 0;
}
5,6
https://onlinegdb.com/S169EF22BТак все, UB нету?
Только, если куда, что передавать надумаете, то либо переменные(tt32_1,tt32_2), либо *(reinterpret_cast<uint32_t*>(&(tt1[0])))
И ещё, изначально было, что "Если же захотите uint64_t разбить на два uint32_t, появляются сложности.", я сказал, что сложностей не вижу. И я их не вижу. Нет сложностей не передавать их в функцию специально особо изощренным способом, чтобы свести с ума компилятор, сложностей нет. И уж на крайний случай -O0 тоже сложностей нет. Но я не говорил, что это надо делать, наоборот, убежден, что не надо.