In questa risposta , zwol ha affermato:
Il modo corretto di convertire due byte di dati da una fonte esterna in un intero con segno a 16 bit è con funzioni di supporto come questa:
#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;
}
Quale delle funzioni sopra è appropriata dipende dal fatto che l'array contenga una rappresentazione little endian o big endian. L'endianness non è il problema in questione qui, mi chiedo perché zwol sottrae 0x10000udal uint32_tvalore convertito int32_t.
Perché è questo il modo corretto ?
Come evita il comportamento definito dall'implementazione durante la conversione nel tipo restituito?
Dato che puoi assumere la rappresentazione del complemento di 2, come fallirebbe questo cast più semplice: return (uint16_t)val;
Cosa c'è di sbagliato in questa ingenua soluzione:
int16_t le16_to_cpu_signed(const uint8_t data[static 2]) {
return (uint16_t)data[0] | ((uint16_t)data[1] << 8);
}
int16_t
0xFFFF0001unon può essere rappresentato come int16_t, e nel secondo approccio 0xFFFFunon può essere rappresentato come int16_t.
int16_tè definito dall'implementazione, quindi l'approccio ingenuo non è portatile.