c++ - long - типы данных си




Является ли std:: memcpy между различными тривиально копируемыми типами неопределенным поведением? (2)

Я давно использую std::memcpy чтобы обойти строгие псевдонимы .

Например, проверка float , например:

float f = ...;
uint32_t i;
static_assert(sizeof(f)==sizeof(i));
std::memcpy(&i, &f, sizeof(i));
// use i to extract f's sign, exponent & significand

Однако на этот раз я проверил стандарт, я не нашел ничего, что подтверждает это. Все, что я нашел, this :

Для любого объекта (кроме потенциально перекрывающегося подобъекта) тривиально копируемого типа T, независимо от того, содержит ли объект допустимое значение типа T, базовые байты ([intro.memory]), составляющие объект, могут быть скопированы в массив символов char, unsigned char или std :: byte ([cstddef.syn]). 40 Если содержимое этого массива копируется обратно в объект, объект должен впоследствии сохранить свое первоначальное значение. [ Пример:

#define N sizeof(T)
char buf[N];
T obj;                          // obj initialized to its original value
std::memcpy(buf, &obj, N);      // between these two calls to std​::​memcpy, obj might be modified
std::memcpy(&obj, buf, N);      // at this point, each subobject of obj of scalar type holds its original value

- конец примера]

и this :

Для любого тривиально копируемого типа T, если два указателя на T указывают на разные объекты T obj1 и obj2, где ни obj1, ни obj2 не являются потенциально перекрывающимся подобъектом, если нижележащие байты ([intro.memory]), составляющие obj1, копируются в obj2, 41 obj2 должен впоследствии иметь то же значение, что и obj1. [ Пример:

T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject of trivially copyable type in *t1p contains
// the same value as the corresponding subobject in *t2p

- конец примера]

Таким образом, std::memcpy позволяет std::memcpy float в / из char[] и std::memcpy между одинаковыми тривиальными типами.

Мой первый пример (и связанный ответ) хорошо определен? Или правильный способ проверки float - это std::memcpy в unsigned char[] буфер unsigned char[] и использовать shift s и or s для создания из него uint32_t ?

Примечание: просмотр гарантий std::memcpy может не дать ответа на этот вопрос. Насколько я знаю, я мог бы заменить std::memcpy на простой цикл копирования байтов, и вопрос будет таким же.


Мой первый пример (и связанный ответ) хорошо определен?

Поведение не определено (если только у целевого типа нет представлений ловушек , которые не являются общими для исходного типа), но полученное значение целого числа определяется реализацией. Стандарт не дает никаких гарантий относительно того, как представлены числа с плавающей запятой, поэтому нет способа извлечь мантиссу и т. Д. Из целого числа переносимым способом - при этом ограничение на использование IEEE 754 с использованием систем в наши дни не сильно ограничивает вас.

Проблемы с переносимостью:

  • IEEE 754 не гарантируется C ++
  • Порядковый номер байта с плавающей запятой не гарантированно совпадает с целочисленным
  • (Системы с представлениями ловушек ).

Вы можете использовать std::numeric_limits::is_iec559 чтобы проверить, std::numeric_limits::is_iec559 ли ваше предположение о представлении.

Хотя, похоже, что у uint32_t не может быть ловушек (см. Комментарии), поэтому вам не нужно об этом беспокоиться. Используя uint32_t , вы уже исключили переносимость в эзотерические системы - стандартные согласующие системы не требуют определения этого псевдонима.


Ваш пример четко определен и не нарушает строгий псевдоним. std::memcpy четко заявляет:

Копирует count байтов из объекта, на который указывает src, в объект, на который указывает dest. Оба объекта интерпретируются как массивы unsigned char .

Стандарт позволяет создавать псевдонимы любого типа через (signed/unsigned) char* или std::byte и, таким образом, ваш пример не показывает UB. Если полученное целое число имеет какое-либо значение, это другой вопрос.

use i to extract f's sign, exponent & significand

Это, однако, не гарантируется стандартом, так как значение с float определяется реализацией (в случае IEEE 754 это будет работать, хотя).







strict-aliasing