W tej odpowiedzi , Zwol wykonane tego twierdzenia:
Prawidłowym sposobem konwersji dwóch bajtów danych ze źródła zewnętrznego na 16-bitową liczbę całkowitą ze znakiem jest użycie funkcji pomocniczych takich jak to:
#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;
}
Która z powyższych funkcji jest odpowiednia, zależy od tego, czy tablica zawiera małą reprezentację endianową, czy dużą. Bajt nie jest sprawa na pytanie tutaj, zastanawiam się dlaczego Zwol odejmuje 0x10000u
od uint32_t
wartości przekształca się int32_t
.
Dlaczego to jest właściwy sposób ?
Jak uniknąć zachowania zdefiniowanego w implementacji podczas konwersji na typ zwracany?
Ponieważ możesz założyć reprezentację dopełniacza 2, dlaczego ta prostsza rzutowanie nie powiedzie się: return (uint16_t)val;
Co jest złego w tym naiwnym rozwiązaniu:
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
nie można przedstawić jako int16_t
, aw drugim podejściu 0xFFFFu
nie można przedstawić jako int16_t
.
int16_t
jest zdefiniowane w implementacji, więc naiwne podejście nie jest przenośne.