AVRのCコードでのこのキャストの何が問題になっていますか?


8

2つの変数を定義しました。

uint8_t a[2];
uint16_t b;

次にa、型の変数として使用しますuint16_t。例:

b = (uint16_t)a;

しかし、これは間違っています!私のプログラムはそのようなコードでは正しく動作しません。要素ごとの操作に置き換えbuint8_t b[2]使用すると、すべて問題ありません。

どうして?

avr  c 

5
あなたの例にいくつかの値を投げて、あなたの「正しい」の期待が何であるかを教えて、意味的な意図について推測することなく実際に私たちが助けることができるようにしてください。
vicatcu 2012

1
これはスタックオーバーフローにはるかに適しています。
シャープトゥース'19

回答:


16

aバイトの配列へのポインタです。あなたはuint16_tにキャストして、それを割り当てた場合b、その後、bSRAMに(それが格納されている)、配列のベースのアドレスが含まれています。配列の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ではなく、組み込みのみの問題でもありません。パソコン用に書かれたアプリケーション・レベルのネットワークコードは、典型的には、呼び出された関数を呼び出してhtonlhtons(ネットワークのホストは、32ビットおよび16ビットの変異体)とntohlntohs(ホストへのネットワーク、32ビットおよび16ビットの変異体)は、その実装彼らかどうかについての依存ターゲットアーキテクチャでありますバイトをスワップするかどうか(「回線上」で送信されるバイトは、マルチバイトワードの一部である場合は常にビッグエンディアンであるという仮定の下)。


これは素晴らしい答えです。aそれ自体が' 'であるキーはポインタです。
Jon L

2
「ユニオンを使用せずにこれを行う別の方法は、aをuint16_tポインターにキャストしてから、それを逆参照することです」 -実際、このタイプのキャストは、多くの場合、厳密なエイリアス規則に違反します。でコンパイルしているのでない限り、それを行うべきではありません-fno-strict-aliasing
ジム・パリ

3

2つの8ビット変数を16ビット変数に連結する場合は、を使用しunionます。aintoの単一のメンバーをにキャストする場合bは、使用する配列の要素を指定します。


2

あなたのコードでは、配列へのポインターのみをキャストしています。

が指す値をキャストする必要がありますa

b = (uint16_t)*a;

私はAVRを使用したことはありませんが、16ビットアーキテクチャで作業している場合は、ワードが整列されていることを確認する必要があります。これを行わないと、例外が発生する場合があります。


1
...これは彼の意図ではありません...彼はbがaの両方の要素に関連付けられることを望んでいます(これはa [1]を完全に無視します)。また、avr-gccでキャストできるものに例外や制限はありません。
vicatcu 2012

0

aの各メンバーは8ビットの数値です。それより大きなものを保持することはできません。16ビットにそれをキャストするには何も行いません。これは、aが保持できるすべての値を抽出し、16ビットに変換して、値がそこに格納されるときにbの形式と一致するようにします。

あなたものメンバーを指すものではありませんでした。a [0]またはa [1]を使用する必要があります(a [2]ではありません!)。単独で使用する場合は、アドレスを取得するだけです。(グッドキャッチ、ブルーノ)。

aを2つの8ビット数の配列として宣言ても、16ビット数にはなりません。プログラムによって、8ビットのシーケンスを使用して16ビット値を格納および取得するためにいくつかのことを行うことができますが、あなたが考えていた方法はできません。


0

バイトをa16ビット値に変換し、表現がリトルエンディアンである場合(値の下位8ビットが最初のバイトにある)、次のようにします。

uint16_t b = a[0] | (a[1] << 8);

ビッグエンディアン表現の場合は、

uint16_t b = (a[0] << 8) | a[1];

移植性の問題が発生するため、これを行うためにポインターのキャストまたはユニオンを使用しないでください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.