7

32ビット組み込みシステム(具体的にはcontikiOSを実行しているredbeeeconotag )でポインターをキャストおよび変更するときに、奇妙な問題が発生しました。

uint32_t array[2];
array[0] = 0x76543210;
array[1] = 0xfedcba98;

uint8_t* point = ((uint8_t*)array)+1;

printf("%08x \n", *(uint32_t*)point );

私のコンピューターでの出力:

98765432

組み込みデバイスでの出力:

10765432

私のコンピュータは期待どおりに動作しますが、組み込みデバイスは単語の終わりに達するとラップアラウンドするようです。なぜこれが起こるのですか?

4

5 に答える 5

13

このコードを使用すると、厳密なエイリアシングルールに違反します。が指すオブジェクトは、型pointを持つ左辺値式によってアクセスされますuint32_t

C11(n1570)、§6.5式
オブジェクトは、次のいずれかの型を持つ左辺値式によってのみ、格納された値にアクセスする必要があります。—オブジェクト
の有効型と互換性のある型— —と互換性のある型
の修飾バージョンオブジェクトの有効な型
—オブジェクトの有効な型に対応する符号付きまたは符号なしの型— —オブジェクトの有効な型
の修飾バージョンに対応する符号付きまたは符号なしの型
— —メンバーの中に前述のタイプの1つを含む集合体または共用体タイプ(再帰的に、サブ集合体または含まれる共用体のメンバーを含む)、または
—文字タイプ。

これは未定義の動作につながるため、何が起こる可能性もあります。

C11(n1570)、§4。適合性
制約またはruntimeconstraintの外側に表示される「shall」または「shallnot」要件に違反した場合、動作は定義されません。

于 2012-12-25T16:53:12.260 に答える
13

ターゲットの「redbeeeconotag」は、ARMv4アーキテクチャを備えたARM7として示されています。ARMv4は、ARMv7やIntelマシンのような非整列メモリアクセスを提供しません。

ARMのドキュメントからの引用:

ARMv4およびARMv5アーキテクチャ、およびARMv6アーキテクチャでは、構成方法に応じて、メモリ内の整列されていないデータにアクセスするときに、予期しない結果が返されないように注意する必要があります。たとえば、従来のポインタを使用してCまたはC ++ソースコードの単語を読み取る場合、ARMコンパイラはLDR命令を使用して単語を読み取るアセンブリ言語コードを生成します。これは、アドレスが4の倍数である場合、たとえば、単語の境界上にある場合に、期待どおりに機能します。ただし、アドレスが4の倍数でない場合、LDRは、真のアラインされていないワードロードを実行するのではなく、ローテーションされた結果を返します。一般に、このローテーションはプログラマーが期待するものではありません

于 2012-12-26T02:14:47.233 に答える
6

+1あなたのために、32ビット値のアラインされていないアクセスを行います。つまり、アドレスは4の倍数ではありません。

x86は、そのルーツが8ビットマシンにまでさかのぼるため、アライメントとは独立して機能します(パフォーマンスがわずかに低下する可能性があります)。

ARMは(他の多くのプロセッサと同様に)アライメントを必要とするため、32ビット値は4バイトの倍数であるアドレスに配置する必要があります。そうでない場合、さまざまな悪いことが起こる可能性があります(間違った値、障害)。配列の場合、コンパイラがそれを処理しますが、ポインタを明示的にキャストすると、強制的に配置に違反します。

于 2012-12-25T21:34:03.153 に答える
5
printf("%08x \n", *(uint32_t*)point );

このステートメントの*式は、未定義の動作を呼び出します。エイリアスルールに違反し、整列されていないアクセスを行う可能性があります。

于 2012-12-25T16:53:03.233 に答える
2

編集:この回答の本文は、それが促したコメントによって無関係になっていることに注意してください

他の答えの理論は問題ありませんが、おそらくあなたを助けません。実際の問題はあなたが書いたことです:

uint8_t* point = ((uint8_t*)array)+1;

次のようなものを書くべきだったとき

uint8_t* point = (uint8_t*)(array+1);

他の何かにキャストする前に、ポインタを適切な型へのポインタとしてインクリメントする必要があるため(インクリメント操作によって要素のサイズが追加されるように)。

しかし、32ビット値へのバイトポインタを本当に使用するつもりかどうかを尋ねる人もいるかもしれません。おそらく、バイト単位でアクセスするつもりです(バイトの順序はシステムによって異なることに注意してください)。あるいは、pointが32ビット値へのポインタであり、それが別の場所にある8ビット値へのポインタになることを本当に意図していたのかもしれません...

于 2012-12-25T20:59:57.230 に答える