Перенос данных данных
Я хочу переинтерпретировать данные одного типа как другого типа переносимым способом (C99).
Я не говорю о кастинге, я хочу переосмыслить некоторые данные.
Кроме того, переносимым я имею в виду, что он не нарушает правила C99 - я не имею в виду, что переопределенное значение равно для всех систем.
Я знаю 3 разных способа переинтерпретировать данные, но только два из них переносимы:
-
Это не переносимо - это нарушает правило строгого сглаживания.
/* #1 Type Punning */
float float_value = 3.14;
int *int_pointer = (int *)&float_value;
int int_value = *int_pointer;
-
Это зависит от платформы, потому что он считывает значение int
из объединения после записи float
в него. Но он не нарушает правил C99, поэтому он должен работать (если sizeof(int) == sizeof(float)
).
/* #2 Union Punning */
union data {
float float_value;
int int_value;
};
union data data_value;
data_value.float_value = 3.14;
int int_value = data_value.int_value;
-
Должно быть хорошо, если sizeof(int) == sizeof(float)
/* #3 Copying */
float float_value = 3.14;
int int_value = 0;
memcpy(&int_value, &float_value, sizeof(int_value));
Мои вопросы:
- Правильно ли это?
- Знаете ли вы другие способы переинтерпретации данных переносимым образом?
Ответы
Ответ 1
Решение 2 является переносимым - произвольное использование пулов через союзы всегда было законным в C99, и оно было четко изложено в TC3, в котором добавлена следующая сноска к разделу 6.5.2.3:
Если элемент, используемый для доступа к содержимому объекта объединения, не является тот же самый, что и последний элемент, используемый для хранения значения в объекте, Соответствующая часть объектного представления значения равна реинтерпретируется как представление объекта в новом типе, как описано в 6.2.6 (процесс, иногда называемый "пингом типа" ). Это может быть ловушки.
Приложение J по-прежнему перечисляет его как непоказанное поведение, которое является известным дефектом и было исправлено с помощью C11, который изменил
Значение члена объединения, отличного от последнего, сохраненного в [не указывается]
к
Значения байтов, которые соответствуют членам объединения, отличным от последнего сохраненного в [не указаны]
Это не такая уж большая сделка, поскольку приложение является только информативным, а не нормативным.
Помните, что вы по-прежнему можете иметь поведение undefined, например
- создав представление ловушки
- нарушая правила сглаживания в случае членов с типом указателя (который не должен быть преобразован с помощью произвольного ввода типа, так как не должно быть равномерного представления указателя)
- если члены профсоюза имеют разные размеры - только байты последнего элемента, используемые в магазине, имеют указанное значение; в частности, сохранение значений в меньшем члене также может привести к недействительности конечных байтов более крупного элемента
- если элемент содержит байты заполнения, которые всегда принимают неопределенные значения
Ответ 2
-
Решение объединения определено как memcpy в C (AFAIK, это UB в С++), см. DR283
-
Можно указать указатель на указатель на (signed/unsigned/) char, поэтому
unsigned char *ptr = (unsigned char*)&floatVar;
а затем доступ к ptr [0] в ptr [sizeof (floatVar) -1] является законным.
Ответ 3
чтобы быть в безопасности, я бы пошел с байтовым массивом (unsigned char), а не с 'int', чтобы удерживать значение.
Ответ 4
тип данных int
является примером непереносимого типа, так как endianness может изменять порядок байтов между платформами.
если вы хотите быть портативным, вам нужно определить свои собственные типы, а затем реализовать их на каждой платформе, к которой вы хотите подключиться. Затем определите методы преобразования для ваших типов данных. Это, насколько я знаю, единственный способ иметь полный контроль над байтовыми заказами и т.д.
Ответ 5
Если вы хотите избежать правила строгого сглаживания, вам нужно сначала направить указатель char:
float float_value = 3.14;
int *int_pointer = (int *)(char *)&float_value;
int int_value = *int_pointer;
Обратите внимание, что у вас может быть sizeof(int) > sizeof(float)
, и в этом случае вы по-прежнему получаете поведение undefined