符号なしタイプから同じランクの符号付きタイプにキャストすると、実装定義の値が生成されることを理解しています。
Cの符号形式が実装定義であるため、実装定義になります。たとえば、2の補数は、そのような実装定義形式の1つです。
したがって、ここでの唯一の問題は、トランスミッションのいずれかの側が2の補数ではないかどうかです。これは、現実の世界では起こりそうにありません。私は、暗黒時代から自分の補数のコンピューターを不明瞭にし、絶滅させるために移植可能なプログラムを設計することを気にしません。
これは、符号付き数値をバイトスワップする方法がわからないことを意味します。たとえば、周辺機器から2バイト、2の補数の符号付き値をリトルエンディアンの順序で受け取り、それらをビッグエンディアンのCPUで処理するとします。
ここでの混乱の原因は、一般的な2の補数が、大きいまたは小さいエンディアンの送信者から送信され、大きい/小さい送信者によって受信されると考えていることだと思います。ただし、データ送信プロトコルはそのようには機能しません。エンディアンと符号の形式を明示的に指定します。したがって、双方がプロトコルに適応する必要があります。
そして、それが指定されると、ここにはロケット科学は実際にはありません。2つの生のバイトを受け取っています。それらを生データの配列に格納します。次に、それらを2の補数変数に割り当てます。プロトコルがリトルエンディアンを指定したとします。
int16_t val;
uint8_t little[2];
val = (little[1]<<8) | little[0];
ビットシフトには、エンディアンに依存しないという利点があります。したがって、上記のコードは、CPUが大きいか小さいかに関係なく機能します。したがって、このコードには醜い暗黙のプロモーションがたくさん含まれていますが、100%移植可能です。Cは、上記を次のように扱うことが保証されています。
val = (int16_t)( ((int)((int)little[1]<<8)) | (int)little[0] );
シフト演算子の結果タイプは、プロモートされた左オペランドのタイプです。|の結果タイプ バランス型(通常の関節変換)です。
符号付きの負の数をシフトすると未定義の動作が発生しますが、個々のバイトが符号なしであるため、シフトを回避できます。それらが暗黙的に昇格された場合でも、数値は正として扱われます。
また、int
少なくとも16ビットであることが保証されているため、コードはすべてのCPUで機能します。
または、すべての暗黙的なプロモーション/変換を完全に除外するペダンティックスタイルを使用することもできます。
val = (int16_t) ( ((uint32_t)little[1] << 8) | (uint32_t)little[0] );
しかし、これには読みやすさが犠牲になります。