Ответ 1
Если вы действительно хотите уважать стандарт, вы должны знать, что код, который вы написали, - это undefined поведение: С++ standard §3.8 [basic.life]:
... за исключением того, что если объект является членом объединения или его подобъектом, его время жизни начинается только в том случае, если этот член объединения является инициализированным членом в союзе (8.6.1, 12.6.2) или как описано в 9.3. Время жизни объекта o тип T заканчивается, когда: (1.3) - если T - тип класса с нетривиальным деструктором (12.4), начинается вызов деструктора или (1.4) - хранилище, которое занимает объект, освобождается или повторно используется объектом, который не вложен в o (1.8).
В §9.3 объясняется, что вы можете активировать член стандартного союза макета, назначив ему. Это также объясняет, что вы можете изучить значение члена объединения, который не активируется только при соблюдении определенных критериев:
Если соединение стандартного макета содержит несколько структур стандартной компоновки, которые имеют общую начальную последовательность (9.2), и если нестатические данные член объекта этого типа стандартного типа макета является активным и является одной из структур стандартной компоновки, разрешено проверять общую начальную последовательность любого из элементов структуры стандартного макета; см. 9.2. - конечная нота]
Итак, когда вы пишете std::cout<< a << "\n"
, вы не инициализировали a
или активировали его назначением, и ни один член не был инициализирован, поэтому вы находитесь в Undefined Поведение (Nota: но компиляторы, которые я знаю, поддерживают его, по крайней мере, на ПК, как расширение стандарта.)
Поэтому перед использованием a
вам нужно будет написать a=0
или сделать a
инициализированным членом объединения, поскольку a
не имеет общей последовательности инициализации ни с b
, ни с c
.
Итак, , если вы используете memset
, также предлагаемый в ответе MSalters, что бы вы ни делали, вам придется назначить что-то члену союза перед его использованием. Если вы хотите оставаться в определенном поведении, не используйте memset
. Обратите внимание, что memset
можно безопасно использовать со стандартным объектом макета, который не является членом союза, поскольку их жизненный цикл начинается, когда для них получено хранилище.
В заключение, чтобы остаться в определенном поведении, вы должны хотя бы инициализировать один элемент, тогда вы можете проверить других членов профсоюза, которые имеют общую последовательность инициализации с инициализированным членом.
-
Если вы намерены использовать анонимный союз в основной функции, вы можете объявить статический союз: все статические объекты инициализируются нулем. (Но не переинициализируются, когда вы вспоминаете функцию, которая не будет выполняться с
main()
):int main(){ static union { s b; int a; char c; }; //... }
Как описано в стандарте С++ §8.6, статья (6.3) [dcl.init]:
если T является (возможно, cv-квалифицированным) типом объединения, объекты первого нестатического именованного элемента данных равны нулю, инициализируется и заполняется инициализируется нулевыми битами;
-
В противном случае, если между элементами структур не существует (
s
), вы можете агрегировать инициализацию с пустым списком большего члена (s
)://... int main(){ union { int a; s b{}; char c; }; //... }
Эта работа, потому что все члены профсоюзов выровнены. Поэтому, если между членами
s
не существует прокладки, каждый байт памяти объединения будет инициализирован нолем, стандарт С++ §9.3 [class.union] статья 2:Размер объединения достаточен для того, чтобы содержать самый большой из его нестатических элементов данных. Каждая нестатическая информация член распределяется так, как если бы он был единственным членом структуры. [Примечание: объект объединения и его нестатические данные члены являются взаимопереключаемыми с указателем (3.9.2, 5.2.9). Как следствие, все нестатические члены данных объединения объект имеет тот же адрес.
-
Если внутри S есть надстройка, просто объявите массив char для цели инициализации:
//... int main(){ union { char _initialization[sizeof(s)]{}; int a; s b; char c; }; //... }
Примечание. Используя ваш пример или два последних примера кода, а код с помощью memset
создает тот же самый набор инструкций для инициализации (clang → x86_64):
pushq %r14
pushq %rbx
subq $120, %rsp
xorps %xmm0, %xmm0
movaps %xmm0, 96(%rsp)
movaps %xmm0, 80(%rsp)
movaps %xmm0, 64(%rsp)
movaps %xmm0, 48(%rsp)
movaps %xmm0, 32(%rsp)
movaps %xmm0, 16(%rsp)
movq $0, 109(%rsp)