Объединения С++ против reinterpret_cast - программирование
Подтвердить что ты не робот

Объединения С++ против reinterpret_cast

Он появляется из qaru.site/info/328644/... и чтения и раздела 9.5.1 ISO/Стандарт стандарта HTML стандарта С++, что использование объединений для выполнения литерала reinterpret_cast данных - это поведение undefined.

Рассмотрим приведенный ниже код. Цель состоит в том, чтобы взять целочисленное значение 0xffff и буквально интерпретировать его как последовательность бит в плавающей точке IEEE 754. (Двоичный конверт визуально показывает, как это делается.)

#include <iostream>
using namespace std;

union unionType {
    int myInt;
    float myFloat;
};

int main() {

    int i = 0xffff;

    unionType u;
    u.myInt = i;

    cout << "size of int    " << sizeof(int) << endl;
    cout << "size of float  " << sizeof(float) << endl;

    cout << "myInt          " << u.myInt << endl;
    cout << "myFloat        " << u.myFloat << endl;

    float theFloat = *reinterpret_cast<float*>(&i);
    cout << "theFloat       " << theFloat << endl;

    return 0;
}

Ожидается вывод этого кода с использованием компиляторов GCC и clang.

size of int    4
size of float  4
myInt          65535
myFloat        9.18341e-41
theFloat       9.18341e-41

Мой вопрос: действительно ли стандарт исключает детерминированность значения myFloat? Является ли использование reinterpret_cast лучше каким-либо образом для выполнения этого типа преобразования?

В стандарте указано следующее в разделе: 9.5.1:

В объединении не более одного из нестатических членов данных может быть активным в любое время, то есть значение не более одного из нестатических элементов данных может быть сохранено в объединении в в любое время. [...] Размер объединения достаточен для размещения самого большого из его нестатических элементов данных. Каждый нестатический элемент данных распределяется так, как если бы он был единственным членом структуры. Все нестатические члены данных объединенного объекта имеют одинаковый адрес.

Последнее предложение, гарантирующее, что все нестатические члены имеют один и тот же адрес, похоже, указывает на то, что использование союза гарантированно будет идентичным использованию reinterpret_cast, но более раннее утверждение об активных членах данных кажется чтобы исключить эту гарантию.

Итак, какая конструкция правильнее?

Edit: Используя компилятор Intel icpc, приведенный выше код дает еще более интересные результаты:

$ icpc union.cpp
$ ./a.out
size of int    4
size of float  4
myInt          65535
myFloat        0
theFloat       0
4b9b3361

Ответ 1

Причина, по которой он undefined заключается в том, что нет гарантии того, что именно представляют представления значений int и float. Стандарт С++ не говорит о том, что float хранится как число с плавающей точкой с одной точностью IEEE 754. Что именно должен сказать стандарт о том, что вы рассматриваете объект int со значением 0xffff как float? Он не говорит ничего, кроме факта undefined.

Фактически, это цель reinterpret_cast - сообщить компилятору игнорировать все, что он знает о типах объектов, и доверять вам, что этот int на самом деле является float. Он почти всегда используется для машинного набора бит-уровня jiggery-pokery. Стандарт С++ просто не гарантирует вам ничего, как только вы это сделаете. В этот момент вам нужно точно понять, что делает ваш компилятор и машина в этой ситуации.

Это справедливо для подходов union и reinterpret_cast. Я предлагаю, чтобы reinterpret_cast был "лучше" для этой задачи, поскольку он делает цель более четкой. Тем не менее, сохранение четкого кода всегда является наилучшим подходом.

Ответ 2

Это не поведение undefined. Это реализация определенного поведения. Первое означает, что могут произойти плохие вещи. Другое означает, что то, что произойдет, должно быть определено реализацией.

Reinterpret_cast нарушает правило строгого сглаживания. Поэтому я не думаю, что это будет работать надежно. Союзный трюк - это то, что люди называют type-punning и обычно разрешено компиляторами. Документы gcc документируют поведение компилятора: http://gcc.gnu.org/onlinedocs/gcc/Structures-unions-enumerations-and-bit_002dfields-implementation.html#Structures-unions-enumerations-and-bit_002dfields-implementation

Я думаю, что это должно работать и с icpc (но они, похоже, не документируют, как они реализовали это). Но когда я посмотрел сборку, похоже, что icc пытается обмануть с помощью float и использовать материал с высокой точностью с плавающей запятой. Передача -fp-model source компилятору исправлена. С этим вариантом я получаю те же результаты, что и с gcc. Я не думаю, что вы хотите использовать этот флаг в целом, это всего лишь тест для проверки моей теории.

Итак, для icpc я думаю, что если вы переключите свой код с int/float на long/double, type-punning будет работать и на icpc.

Ответ 3

Undefined поведение не означает, что плохие вещи должны произойти. Это означает только, что определение языка не говорит вам, что происходит. Этот тип каламбуров был частью программирования на C и С++ с незапамятных времен (т.е. С 1969 года); для написания компилятора, в котором это не сработало, потребовался бы особенно порочный разработчик.