Именно за счет расширений и можно писать понятный код и писать его проще, ежели как это позволял бы голый стандарт.
Да, в общем случае UB может привести к любым результатам - как правильным, так и неправильным. Но практически все современные компиляторы имеют расширения. Всякие __unaligned, __packed, и т.д. - все эти ключевые слова отсутствуют в стандарте, но их четко описывают в документации на компилятор. Тогда с чего компилятор будет генерировать UB?
В прошлый раз (в теме, которую Вы указали) мы обсуждали не UB по выравниванию, а UB из-за доступа к volatile между двумя точками следования. У компилятора на этот счет нет описания в справочнике, а значит имеет смысл делать предположения о следовании его стандарту. Вот и все.
Касательно расширений продолжу.
u32 *p = 'address';
Компилятору никаких специальных указаний нет, поэтому он предполагает, что 'p' содержит валидный выровненный указатель. При доступе будет сгенерирована команда чтения/записи 32-битного слова (или кучка команд доступа по частям) - имеет право. Если указатель не выровнен, то поведение не определено: если компилятор уже знает о том, что адрес не выровнен, то ассемблерный листинг может весьма удивить. В противном случае лишь юзер может знать, во что выльется (возможное) применение невыровненного доступа на его процессоре.
__unaligned u32 *p = 'address';
Компилятор заранее наделили знанием, что указатель может содержать невыровненный адрес. Тут UB с точки зрения выравнивания нет. Компилятор сгенерирует любую из удобных (поддерживаемых CPU) инструкций доступа по адресу в 'p'.
u32 volatile *p = 'address';
Компилятор предполагает, что 'p' содержит выровненный указатель. В отличие от первого случая, процессор не может читать/писать значение по указателю "частями", потому что это нарушит семантику обращений/изменений volatile-объекта между двумя точками следования. Поэтому компилятор строго предполагает, что адрес тут точно выровнен, и никак иначе. Он однозначно генерирует инструкцию 32-битного доступа. Если же в этот указатель каким-то образом поместился невыровненный указатель (динамически, в run-time), то, как и в первом случае, лишь юзеру будет ясно, чем это чревато. Если же компилятор на этапе компиляции уже будет видеть неровный указатель - UB, может такой доступ запросто вовсе выкинуть из кода, прихватив все зависимое.
__unaligned u32 volatile *p = 'address';
Итак, у 'p' два атрибута: __unaligned и квалификатор volatile у объекта, на который он показывает. Их бы надо как-то поженить между собой. Однако это невозможно, т.к. невыровненный доступ по указателю, помеченному __unaligned, вроде, и определен, но volatile накладывает свое "вето" на эту невыровненность, потому что иначе сломается семантика доступа к volatile. Т.е. компилятор должен тут решить, как ему поступать в данном случае: 1) "закрыть глаза" на строгую семантику доступа к volatile в части кол-ва обращений к объекту (при этом не оптимизировать сам доступ), разрешая таким образом работу с невыровненным указателем; 2) пытаться соблюсти корректный доступ к volatile-объекту, а это ничто иное как обеспечить однократный доступ к памяти инструкцией соответствующей разрядности доступа - а для этого нужно отбросить __unaligned и "задним числом" положиться на программиста, что указатель гарантированно выровнен. Если же юзер указатель не выровнил, то вылезет UB по отношению к volatile-объекту (ведь по отношению к обычному доступу UB уже "заранее" парирован ключевым словом __unaligned).
Судя по листингам, по первому пути пошел GCC, а по второму LLVM/CLang, который мне, в свою очередь, и кажется более правильным.
Вот если есть __unaligned или что-то подобное в компиляторе, то проблем нет. Напрямую "натягивать" указатель типа структуры на память может выйти боком при strict aliasing, но эта проблема решаема: в C++, например, с помощью placement new.