2016-04-27 282 views
3

2バイト配列を単一の16ビット値に変換しようとしています。何らかの理由で、配列を16ビットのポインタとしてキャストして逆参照すると、値のバイト順序が入れ替えられます。例えばuint8_t配列をC言語のuint16_t値にキャストする

、(私が期待するものである)

#include <stdint.h> 
#include <stdio.h> 

main() 
{ 
    uint8_t a[2] = {0x15, 0xaa}; 

    uint16_t b = *(uint16_t*)a; 
    printf("%x\n", (unsigned int)b); 
    return 0; 
} 

プリントaa15代わりに15aa

これの背景には何があり、簡単に修正できますか?

私はuint16_t b = a[0] << 8 | a[1];(これはうまくいきます)のようなことができることを知っていますが、この問題がキャスティングで簡単に解決できるはずです。ここで何が原因か分かりません。

+6

これは[エンディアン](https://en.wikipedia.org/wiki/Endianness)によるものです。あなたはキャスティングでそれを解決することはできません:shift-and-add(または)は良いです。 –

+0

http://stackoverflow.com/questions/22030657/little-endian-vs-big-endian – fukanchik

答えて

6

コメントに記載されているとおり、これはendiannessが原因です。

マシンはリトルエンディアンで、マルチバイト整数値の最下位バイトが最初にあることを意味します。

このコードをビッグエンディアンマシン(例:Sun)でコンパイルして実行した場合、期待した結果が得られます。

あなたのアレイはビッグエンディアンとして設定されています(ネットワークバイトオーダーでもあるので、ntohshtonsを使用してこの問題を回避できます)。これらの機能は、ホストのバイト順序とその逆にネットワークバイトオーダー(ビッグエンディアン)から16ビットの値を変換:

uint16_t b = ntohs(*(uint16_t*)a); 

32ビット値で作業ntohlhtonlと呼ばれる同様の機能があります。

+0

このコードに関連しているStackOverflowの上の質問に答える得た私を保証@WeatherVaneバイト0 –

-1

これは物を視覚化するのに役立ちます。配列を作成するときは、2バイトが順番にあります。あなたがそれを印刷すると、それが格納されたリトルエンディアンの方法とは逆の、人間が読める16進値が得られます。 A0がA1よりも下位のアドレスです、次のようにuint16_tタイプが格納されているとして、リトルエンディアンの値1は...

a0  a1 
|10000000|00000000 

注意、最下位バイトが最初であるが、我々は六角でそれを価値を印刷するとき右端に最下位バイトが表示されます。これは、通常どんなマシンでも期待されています。

このプログラムは、少なくともからリトルエンディアンとバイナリの出発で、ビッグエンディアン1上位バイトを出力します...

#include <stdint.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <arpa/inet.h> 

void print_bin(uint64_t num, size_t bytes) { 
    int i = 0; 
    for(i = bytes * 8; i > 0; i--) { 
    (i % 8 == 0) ? printf("|") : 1; 
    (num & 1) ? printf("1") : printf("0"); 
    num >>= 1; 
    } 
    printf("\n"); 
} 
int main(void) { 
    uint8_t a[2] = {0x15, 0xaa}; 
    uint16_t b = *(uint16_t*)a; 
    uint16_t le = 1; 
    uint16_t be = htons(le); 

    printf("Little Endian 1\n"); 
    print_bin(le, 2); 
    printf("Big Endian 1 on little endian machine\n"); 
    print_bin(be, 2); 
    printf("0xaa15 as little endian\n"); 
    print_bin(b, 2); 
    return 0; 
} 

これが出力されます(これは最下位バイトが最初である)

Little Endian 1 
|10000000|00000000 
Big Endian 1 on little endian machine 
|00000000|10000000 
0xaa15 as little endian 
|10101000|01010101 
+0

何質問または2バイトの配列のいずれかで行うには、 'uint64_t'を持っていますか?これは単純な問題を過度に複雑にしているようです。 –

+0

申し訳ありませんが、私のDVは '1 'は決して' 10000000'として保存されていません。 –

+0

私は分かりません。 '100000001'として格納されているとは言いませんでした。左側に最初のバイトが格納されていることを示すために2バイトを使用しました。 – Harry

0

これは、マシンのエンディアンが原因です。機械のあなたのコードの独立したが、以下の機能を検討するためには

#define LITTLE_ENDIAN 0 
#define BIG_ENDIAN 1 

int endian() { 
    int i = 1; 
    char *p = (char *)&i; 

    if (p[0] == 1) 
     return LITTLE_ENDIAN; 
    else 
     return BIG_ENDIAN; 
} 

ですから、適用するためにどの操作を選択することができ、それぞれの場合について。

0

あなたは理由strict aliasing rule*(uint16_t*)aのような何かを行うことはできません。コードが現在動作しているように見えても、別のコンパイラバージョンで後で破損する可能性があります。

コードの正しいバージョンは次のようになります。16ビットintを持つシステム上で、これは符号付き整数オーバーフローを引き起こす可能性があるためa[0] << 8を含む

b = ((uint16_t)a[0] << CHAR_BIT) + a[1]; 

バージョンがあなたの質問で提案が正しくありません:a[0]はに促進しますint、そして<< 8* 256を意味します。

関連する問題