У цій відповіді , 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
визначається реалізацією, тому наївний підхід не є переносним.