У меня создалось впечатление, что доступ к union
члену, отличному от последнего набора, - это UB, но я не могу найти надежную ссылку (кроме ответов, в которых утверждается, что это UB, но без какой-либо поддержки со стороны стандарта).
Итак, это неопределенное поведение?
c++
undefined-behavior
language-lawyer
unions
Лучиан Григоре
источник
источник
Ответы:
Путаница в том, что C явно разрешает выбор типов через объединение, тогда как C ++ (C ++ 11) не имеет такого разрешения.
Ситуация с C ++:
Позднее в C ++ появился язык, позволяющий использовать объединения, содержащие
struct
s с общими начальными последовательностями; это, однако, не позволяет набирать текст.Для того, чтобы определить , является ли объединение типа каламбурным это разрешено в C ++, мы должны искать дальше. Напомним, чтоc99 является нормативным справочником для C ++ 11 (и C99 имеет язык, аналогичный C11, разрешающий использование типов объединения):
Это становится особенно интересно, когда мы читаем
Таким образом, для примитивного типа (который ipso facto имеет тривиальную инициализацию), содержащегося в объединении, время жизни объекта охватывает, по крайней мере, время жизни самого объединения. Это позволяет нам вызывать
Предполагая, что интересующая нас операция - это определение типа, то есть получение значения неактивного члена объединения, и учитывая вышеизложенное, что у нас есть действительная ссылка на объект, на который ссылается этот член, эта операция будет lvalue-to -rvalue преобразование:
Тогда возникает вопрос, инициализируется ли объект, который является неактивным членом объединения, хранилищем для активного члена объединения. Насколько я могу судить, это не так, хотя, если:
char
хранилище массива и обратно (3.9: 2), илидоступ к объединению для неактивного члена определен и определен так, чтобы следовать представлению объекта и значения, доступ без одного из вышеуказанных промежуточных положений является неопределенным поведением. Это имеет значение для оптимизаций, которые могут быть выполнены в такой программе, поскольку реализация, конечно, может предполагать, что неопределенное поведение не возникает.
То есть, хотя мы можем законно сформировать lvalue для неактивного члена объединения (поэтому присвоение неактивному члену без построения - это нормально), оно считается неинициализированным.
источник
memcpy
реализации (доступ к объектам с использованиемunsigned char
lvalues), он запрещал доступ к*p
afterint *p = 0; const int *const *pp = &p;
(даже если неявное преобразование изint**
вconst int*const*
является допустимым), он запрещал даже доступc
послеstruct S s; const S &c = s;
. CWG, выпуск 616 . Допускает ли это новая формулировка? Также есть [basic.lval].&
означает унарный оператор при применении к члену объединения. Я бы подумал, что полученный указатель должен использоваться для доступа к члену, по крайней мере, до следующего прямого или косвенного использования любого другого члена lvalue, но в gcc указатель нельзя использовать даже так долго, что вызывает вопрос о том, что&
оператор должен иметь в виду.Стандарт C ++ 11 говорит об этом так
Если сохраняется только одно значение, как можно прочитать другое? Его просто нет.
В документации gcc это указано в разделе « Поведение, определяемое реализацией».
указывая, что это не требуется стандартом C.
2016-01-05: Через комментарии я был связан с отчетом о дефектах C99 № 283, который добавляет аналогичный текст в качестве сноски к стандартному документу C:
Не уверен, что он многое проясняет, учитывая, что сноска не является нормативной для стандарта.
источник
Я думаю, что ближе всего к стандарту говорится о неопределенном поведении, когда он определяет поведение для объединения, содержащего общую начальную последовательность (C99, §6.5.2.3 / 5):
С ++ 11 дает аналогичные требования / разрешения в §9.2 / 19:
Хотя ни один из них не заявляет об этом прямо, оба они имеют сильное значение, что «проверка» (чтение) члена «разрешена» только в том случае, если 1) это (часть) член, который был написан последним, или 2) является частью общего начального последовательность.
Это не прямое заявление о том, что иное является неопределенным поведением, но это самое близкое из известных мне.
источник
union
s не определено, поскольку в одном конкретном блоге у меня сложилось впечатление, что это нормально, и я построил вокруг этого несколько больших структур и проектов. Теперь я думаю , что со мной все в порядке, так как моиunion
s действительно содержат классы с одинаковыми типами впередиunion
содержит, например, auint8_t
и aclass Something { uint8_t myByte; [...] };
- я предполагаю, что это условие также применимо здесь, но оно сформулировано очень намеренно, чтобы разрешить толькоstruct
s. К счастью, я уже использую их вместо сырых примитивов: OТо, что еще не упоминается в доступных ответах, - это сноска 37 в параграфе 21 раздела 6.2.5:
Это требование явно подразумевает, что вы не должны писать в одном элементе и читать в другом. В этом случае это может быть неопределенное поведение из-за отсутствия спецификации.
источник
Я хорошо объясню это на примере.
Предположим, у нас есть следующий союз:
Я хорошо предполагаю, что это
sizeof(int)
дает 4, а этоsizeof(short)
дает 2.когда вы пишете
union A a = {10}
это хорошо, создайте новую переменную типа A и вставьте в нее значение 10.ваша память должна выглядеть так: (помните, что все члены союза находятся в одном месте)
как вы могли видеть, значение ax равно 10, значение ay 1 равно 10, а значение ay [0] равно 0.
А что будет, если я сделаю это?
наша память будет выглядеть так:
это превратит значение ax в 2424842 (в десятичном виде).
теперь, если в вашем объединении есть float или double, ваша карта памяти будет более беспорядочной из-за того, как вы храните точные числа. больше информации вы можете получить здесь .
источник