Является ли type-punning через объединение неуказанным в C99, и стало ли это указано в C11?

Ряд ответов на вопрос о переполнении стека Получение битов с одиночной точностью IEEE для float предполагает использование структуры union для персонализации типа (например: поворот биты a float в uint32_t):

union {
    float f;
    uint32_t u;
} un;
un.f = your_float;
uint32_t target = un.u;

Однако значение члена uint32_t объединения представляется неуказанным в соответствии со стандартом C99 (по крайней мере, проект n1124), где в разделе 6.2.6.1.7 указано:

Когда значение хранится в члене объекта типа union, байты представления объекта, которые не соответствуют этому элементу, но соответствуют другим членам, принимают неопределенные значения.

По крайней мере одна сноска проекта C11 n1570, по-видимому, подразумевает, что это уже не так (см. сноску 95 в 6.5.2.3):

Если элемент, используемый для чтения содержимого объекта объединения, не совпадает с элементом, который последний раз использовался для сохранить значение в объекте, соответствующая часть представления объекта значения переинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый "типом" каламбурная). Это может быть ловушечное представление.

Однако текст в разделе 6.2.6.1.7 в проекте C99 такой же, как в проекте C11.

Действительно ли это поведение не указано в C99? Стало ли это указано в C11? Я понимаю, что большинство компиляторов, похоже, поддерживают это, но было бы неплохо узнать, указано ли это в стандарте или просто очень распространенное расширение.

Ответы

Ответ 1

Поведение типа punning with union изменено с C89 на C99. Поведение на C99 такое же, как у C11.

Как отметил Угг в своем ответе, тип punning разрешен в C99/C11. Неопределенное значение, которое может быть ловушкой, считывается, когда члены объединения имеют разный размер.

Сноска была добавлена в C99 после отчета Clate DW Feather Defect Report № 257:

Наконец, одним из изменений с C90 на C99 было устранение любых ограничений на доступ к одному члену союза, когда последний магазин был другим. Обоснованием было то, что поведение тогда будет зависеть от представлений значений. Поскольку этот момент часто неправильно понимается, вполне возможно, что это будет понятно в Стандарте.

[...]

Чтобы устранить проблему с "типом punning", прикрепите новую сноску 78a к словам "named member" в 6.5.2.3 № 3: 78a Если член, используемый для доступа к содержимому объекта объединения, не совпадает с последним членом используемый для хранения значения в объекте, соответствующая часть представления объекта значения переинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый "тип punning"). Это может быть ловушечное представление.

Формулировка Clive DW Feather была принята для Технического исправления в ответе Комитета С Комитета по Дефекту № 283.

Ответ 2

Оригинальная спецификация C99 оставила это неуказанным.

Один из технических исправлений к C99 (TR2, я думаю) добавил примечание 82, чтобы исправить этот недосмотр:

Если элемент, используемый для доступа к содержимому объекта объединения, не совпадает с элементом, который последний раз использовался для хранения значения в объекте, соответствующая часть представления объекта значения интерпретируется как представление объекта в новый тип, как описано в 6.2.6 (процесс, иногда называемый "пингом типа" ). Это может быть ловушечное представление.

Эта сноска сохраняется в стандарте C11 (это сноска 95 в C11).

Ответ 3

Это всегда было "iffy". Как отмечали другие, сноска была добавлена ​​на C99 через Техническое исправление. Он читается следующим образом:

Если элемент, используемый для доступа к содержимому объекта объединения, не совпадает с элементом, который последний раз использовался для хранения значения в объекте, соответствующая часть представления объекта значения интерпретируется как представление объекта в новый тип, как описано в 6.2.6 (процесс, иногда называемый "пингом типа" ). Это может быть ловушечное представление.

Однако сноски указаны в предисловии как ненормативные:

Приложения D и F образуют нормативную часть этого стандарта; приложения A, B, C, E, G, H, I, J, библиография и индекс предназначены только для информации. В соответствии с частью 3 Директив ISO/IEC, это предисловие, введение, примечания, сноски и примеры также доступны только для информации.

То есть сноски не могут запрещать поведение; они должны лишь уточнить существующий текст. Это непопулярное мнение, но приведенная выше сноска фактически не справляется в этом отношении - такого поведения запрещается в нормативном тексте. Действительно, существуют такие части, как 6.7.2.1:

... Значение не более одного из членов может быть сохранено в объединенном объекте в любое время

В сочетании с 6.5.2.3 (относительно доступа к членам профсоюза с оператором "." ):

Значение этого имени элемента

т.е. если значение только одного члена может быть сохранено, значение другого элемента не существует. Это сильно означает, что тип punning через объединение не может быть возможным; доступ к члену дает несуществующее значение. Этот же текст все еще существует в документе C11.

Однако ясно, что цель добавления сноски заключалась в том, чтобы разрешить печатание типа; это просто, что комитет, казалось бы, нарушил правила сносок, не содержащих нормативный текст. Чтобы принять сноску, вы действительно должны игнорировать раздел, в котором говорится, что сноски не являются нормативными, или иным способом пытаться выяснить, как интерпретировать нормативный текст таким образом, который поддерживает заключение сноски (которое я пробовал, и не удалось выполнить).

Раздел, который вы цитируете:

Когда значение хранится в члене объекта типа union, байты представления объекта, которые не соответствуют этому элементу, но соответствуют другим членам, принимают неопределенные значения.

... нужно внимательно прочитать. "Байты представления объекта, которые не соответствуют этому элементу", относятся к байтам, превышающим размер члена, что само по себе не является проблемой для кастомизации типа (за исключением того, что вы не можете предположить, что запись члену профсоюза покинет "лишняя" часть любого большего члена нетронутая).

Ответ 4

Однако это, по-видимому, нарушает стандарт C99 (по крайней мере, проект n1124), где в разделе 6.2.6.1.7 содержатся некоторые сведения. Действительно ли это поведение не указано в C99?

Нет, все в порядке.

Когда значение хранится в члене объекта типа union, байты представления объекта, которые не соответствуют этому элементу, но соответствуют другим членам, принимают неопределенные значения.

Это относится к блокам данных разных размеров. I.e, если у вас есть:

union u
{
    float f;
    double d;
};

и вы назначаете что-то f, он изменит более низкие 4 байта d, но верхние 4 байта будут находиться в неопределенном состоянии.

Союзы существуют в основном для типа punning.