5

こんにちは、バイト配列を構造体に読み込もうとしていますが、バイトが逆の順序で出てきます(予想どおり)。誰かが何が起こっているのか理解するのを手伝ってくれますか?

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 (役立つ場合)

ご協力いただきありがとうございます。

4

3 に答える 3

9

endiannessについて読むと、1 バイトよりも長いデータをメモリに格納する方法が 2 つあります。

ビッグ エンディアン システム (ARM など) の場合、整数値0x1122は (下位アドレスから上位アドレスへ) としてメモリに格納されます0x11 0x22。リトルエンディアン システム (x86 など) では、0x22 0x11.

配列内のデータは「ビッグエンディアン」で保存されるため、あなたのようなリトルエンディアンシステムで期待するものとは逆のバイト順になります。

于 2013-07-26T11:55:24.720 に答える
3

他の人が指摘しているように、あなたの主な観察可能な問題は、ediannessの 1 つです。

アクセスする方法は、ここでタイプパニングによって厳密なエイリアシングbufルールに違反しているため、未定義の動作です。

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

gcc次の引数を使用してこのコードを作成した場合:

-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 ドラフト標準の関連セクションは です6.5/7

于 2013-07-26T12:11:56.153 に答える
1

Joachim と 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);
}

編集:

#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
于 2013-07-26T12:45:22.457 に答える