2つの変数を定義しました。
uint8_t a[2];
uint16_t b;
次にa
、型の変数として使用しますuint16_t
。例:
b = (uint16_t)a;
しかし、これは間違っています!私のプログラムはそのようなコードでは正しく動作しません。要素ごとの操作に置き換えb
てuint8_t b[2]
使用すると、すべて問題ありません。
どうして?
2つの変数を定義しました。
uint8_t a[2];
uint16_t b;
次にa
、型の変数として使用しますuint16_t
。例:
b = (uint16_t)a;
しかし、これは間違っています!私のプログラムはそのようなコードでは正しく動作しません。要素ごとの操作に置き換えb
てuint8_t b[2]
使用すると、すべて問題ありません。
どうして?
回答:
a
バイトの配列へのポインタです。あなたはuint16_tにキャストして、それを割り当てた場合b
、その後、b
SRAMに(それが格納されている)、配列のベースのアドレスが含まれています。配列の2バイトをa
整数として扱いたい場合は、user14284の提案に従ってユニオンを使用しますが、ユニオンはアーキテクチャのメモリバイト順でバイト配列を表すことに注意してください(AVRでは、 -endian。これは、バイト0が最下位バイトであることを意味します)。これをコードで記述する方法は次のとおりです。
union{
uint8_t a[2];
uint16_t b;
} x;
x.b[0] = 0x35;
x.b[1] = 0x4A;
// by virtue of the above two assignments
x.a == 0x4A35 // is true
ユニオンを使用せずにこれを行う別の方法はa
、uint16_tポインターにキャストしてから、次のように逆参照することです。
uint8_t a[2] = {0x35, 0x4A};
uint16_t b = *((uint16_t *) a);
b == 0x4A35; // because AVR is little endian
バッファーを使用してビッグエンディアンデータ(ネットワークバイトオーダーなど)を格納している場合、これらの手法のいずれかを使用するにはバイトスワップする必要があります。ブランチや一時変数なしでそれを行う方法は次のとおりです:
uint8_t a[2] = {0x35, 0x4A};
a[0] ^= a[1];
a[1] ^= a[0];
a[0] ^= a[1];
a[0] == 0x4A; // true
a[1] == 0x35; // true
ちなみに、これはAVRではなく、組み込みのみの問題でもありません。パソコン用に書かれたアプリケーション・レベルのネットワークコードは、典型的には、呼び出された関数を呼び出してhtonl
、htons
(ネットワークのホストは、32ビットおよび16ビットの変異体)とntohl
、ntohs
(ホストへのネットワーク、32ビットおよび16ビットの変異体)は、その実装彼らかどうかについての依存ターゲットアーキテクチャでありますバイトをスワップするかどうか(「回線上」で送信されるバイトは、マルチバイトワードの一部である場合は常にビッグエンディアンであるという仮定の下)。
a
それ自体が' 'であるキーはポインタです。
-fno-strict-aliasing
。
あなたのコードでは、配列へのポインターのみをキャストしています。
が指す値をキャストする必要がありますa
。
b = (uint16_t)*a;
私はAVRを使用したことはありませんが、16ビットアーキテクチャで作業している場合は、ワードが整列されていることを確認する必要があります。これを行わないと、例外が発生する場合があります。
aの各メンバーは8ビットの数値です。それより大きなものを保持することはできません。16ビットにそれをキャストするには何も行いません。これは、aが保持できるすべての値を抽出し、16ビットに変換して、値がそこに格納されるときにbの形式と一致するようにします。
あなたものメンバーを指すものではありませんでした。a [0]またはa [1]を使用する必要があります(a [2]ではありません!)。単独で使用する場合は、アドレスを取得するだけです。(グッドキャッチ、ブルーノ)。
aを2つの8ビット数の配列として宣言しても、16ビット数にはなりません。プログラムによって、8ビットのシーケンスを使用して16ビット値を格納および取得するためにいくつかのことを行うことができますが、あなたが考えていた方法はできません。