В этом ответе , zwol сделал это заявление:
Правильный способ преобразования двух байтов данных из внешнего источника в 16-разрядное целое число со знаком - с помощью вспомогательных функций, таких как:
#include <stdint.h>
int16_t be16_to_cpu_signed(const uint8_t data[static 2]) {
uint32_t val = (((uint32_t)data[0]) << 8) |
(((uint32_t)data[1]) << 0);
return ((int32_t) val) - 0x10000u;
}
int16_t le16_to_cpu_signed(const uint8_t data[static 2]) {
uint32_t val = (((uint32_t)data[0]) << 0) |
(((uint32_t)data[1]) << 8);
return ((int32_t) val) - 0x10000u;
}
Какая из вышеуказанных функций подходит, зависит от того, содержит ли массив представление с прямым или обратным порядком байтов. Порядок байтов не является проблемой на вопрос здесь, я задаюсь вопросом, почему zwol вычитает 0x10000u
из uint32_t
значения преобразуются в int32_t
.
Почему это правильный путь ?
Как избежать поведения, определенного при реализации, при преобразовании в тип возвращаемого значения?
Так как вы можете предположить представление дополнения 2, как это простое приведение завершится неудачно: return (uint16_t)val;
Что не так с этим наивным решением:
int16_t le16_to_cpu_signed(const uint8_t data[static 2]) {
return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
}
int16_t
0xFFFF0001u
не может быть представлен как int16_t
, а во втором подходе 0xFFFFu
не может быть представлен как int16_t
.
int16_t
зависит от реализации, поэтому наивный подход не переносим.