2013-07-26 7 views
5

こんにちは私は構造体にバイト配列を読み込もうとしています、そして、バイトは逆の順序で出てきます(私が予想したところまで)。誰かが私に起こっていることを理解するのを助けることができますか?c構造体とバイトの設定/順序付け

unsigned char buf[] = { 
0x11, 0x22, 0x33, 0x44, 
0x55, 0x66, 0x77, 0x88, 
0x99, 0xaa, 0xbb, 0xcc 
}; 


typedef struct mystruct { 
uint16_t var1; 
uint16_t var2; 
uint32_t var3; 
uint32_t var4; 
} something; 


int main(int argc,char **argv){ 

    printf("sizeof buf: %lu %d \n",sizeof(buf),sizeof(something)); 
    something *st = (something*)&(buf[0]); 
    #define pr(a) printf(#a" %x\n",a) 
    pr(st->var1); 
    pr(st->var2); 
    pr(st->var3); 
    pr(st->var4); 

    return(0); 
} 

出力:

sizeof buf: 12 12 
st->var1 2211 
st->var2 4433 
st->var3 88776655 
st->var4 ccbbaa99 

私のようなものを期待していた。 ST-> VAR1これを行う1122

も出力にも同じことですか?

memcpy(&st->var1,buf,2); 
pr(st->var1); 

出力: ST-> VAR1 2211

のx86/Linuxサーバ、gccのバージョン4.5.3(それは場合に役立ちます)あなたの助けを

感謝。

+2

エンディアンについて読むことをお勧めします。たとえばhttp://en.wikipedia.org/wiki/Endiannessでこれを行うことができます。 – alk

答えて

9

endiannessを読むと、メモリに1バイト以上のデータを格納する方法が2つあることがわかります。

ビッグエンディアンシステム(ARMなど)の場合、整数値0x1122は(低い方から高い方へ)0x11 0x22としてメモリに格納されます。リトルエンディアンシステム(x86など)では、0x22 0x11という名前で保存されます。

配列内のデータが "ビッグエンディアン"に格納されるため、あなたのようなリトルエンディアンシステムで期待されるものとは逆のバイト順序が得られます。

+1

厳密なエイリアシングの問題もあります。 –

3

あなたの主な観察可能な問題は、ediannessのうちの1つです。

それはここpunningタイプによってここにstrict aliasing規則に違反しているためbufにアクセスするためのあなたの方法は、未定義の動作です:私は、次の引数を使用してgccを使用してこのコードを内蔵している場合

something *st = (something*)&(buf[0]); 

-O3 --Wstrict-aliasing=2 

次の警告が表示されます。

main.cpp:22:4: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] 
    something *st = (something*)&(buf[0]); 
    ^

私は現在、バージョン4.8を使用しています。エイリアシング規則をカバーするC11 draft standardの関連セクションは6.5/7です。

1

は(スワップバイトリトルエンディアン)のヨアヒムとShafixが言ったように、あなたはパディングとエンディアンの問題を持つことができますが、あなたは#pragma packを利用することができる場合は、このコードは動作するはずです

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

unsigned char buf[] = { 
    0x11, 0x22, 0x33, 0x44, 
    0x55, 0x66, 0x77, 0x88, 
    0x99, 0xaa, 0xbb, 0xcc 
}; 

typedef struct mystruct { 
    uint16_t var1; 
    uint16_t var2; 
    uint32_t var3; 
    uint32_t var4; 
} something; 

static uint16_t swap16(uint16_t val) 
{ 
    return ((val >> 8) & 0xFF) | ((val << 8) & 0xFF00); 
} 

static uint32_t swap32(uint32_t val) 
{ 
    uint16_t v1 = swap16((uint16_t) val); 
    uint16_t v2 = swap16((uint16_t) (val >> 16)); 
    return (v1 << 16) | (v2); 
} 

int main(void) 
{ 
    printf("sizeof buf: %zu %zu \n", sizeof(buf), sizeof(something)); 
    something *st = (something*)&(buf[0]); 

    #define pr(a) printf(#a" %x\n", a) 

#if __BYTE_ORDER == __LITTLE_ENDIAN 
    st->var1 = swap16(st->var1); 
    st->var2 = swap16(st->var2); 
    st->var3 = swap32(st->var3); 
    st->var4 = swap32(st->var4); 
#endif 

    pr(st->var1); 
    pr(st->var2); 
    pr(st->var3); 
    pr(st->var4); 

    return(0); 
} 

EDIT:

#pragma packは、構造体のメンバの所定のアライメントを破棄するようにプリプロセッサの原因となりますので、パディングバイトが挿入されないであろう次のように、あなたはあなたの構造を定義することができます

#pragma pack(push, 1) // exact fit - no padding 
typedef struct mystruct { 
    uint16_t var1; 
    uint16_t var2; 
    uint32_t var3; 
    uint32_t var4; 
} something; 
#pragma pack(pop) //back to whatever the previous packing mode was 
+0

このバージョンには、元のコードと同じ厳密なエイリアシングの問題があります。 –

+0

また、POSIXの 'ntohs'と' ntohl'関数を使うこともできます。 – Useless

+0

@ShafikYaghmour、あなたが '#pragma pack'_ –