9

私はこのC構造体を持っています:(IPデータグラムを表す)

struct ip_dgram
{
    unsigned int ver   : 4;
    unsigned int hlen  : 4;
    unsigned int stype : 8;
    unsigned int tlen  : 16;
    unsigned int fid   : 16;
    unsigned int flags : 3;
    unsigned int foff  : 13;
    unsigned int ttl   : 8;
    unsigned int pcol  : 8;
    unsigned int chksm : 16;
    unsigned int src   : 32;
    unsigned int des   : 32;
    unsigned char opt[40];
};

値を割り当てて、メモリ レイアウトを次のように 16 ビット ワードで出力します。

//prints 16 bits at a time
void print_dgram(struct ip_dgram dgram)
{
    unsigned short int* ptr = (unsigned short int*)&dgram;
    int i,j;
    //print only 10 words
    for(i=0 ; i<10 ; i++)
    {
        for(j=15 ; j>=0 ; j--)
        {
            if( (*ptr) & (1<<j) ) printf("1");
            else printf("0");
            if(j%8==0)printf(" ");
        }
        ptr++;
        printf("\n");
    }
}

int main()
{
    struct ip_dgram dgram;

    dgram.ver   = 4;
    dgram.hlen  = 5;
    dgram.stype = 0;
    dgram.tlen  = 28;
    dgram.fid   = 1;
    dgram.flags = 0;
    dgram.foff  = 0;
    dgram.ttl   = 4;
    dgram.pcol  = 17;
    dgram.chksm = 0;
    dgram.src   = (unsigned int)htonl(inet_addr("10.12.14.5"));
    dgram.des   = (unsigned int)htonl(inet_addr("12.6.7.9"));

    print_dgram(dgram);

    return 0;
}

私はこの出力を得る:

00000000 01010100 
00000000 00011100 
00000000 00000001 
00000000 00000000 
00010001 00000100 
00000000 00000000 
00001110 00000101 
00001010 00001100 
00000111 00001001 
00001100 00000110

しかし、私はこれを期待しています:

ここに画像の説明を入力

出力は部分的に正しいです。どこかで、バイトとニブルが交換されているようです。ここにエンディアンの問題がありますか?ビットフィールドはこの目的には適していませんか? 本当にわかりません。何か助けはありますか?前もって感謝します!

4

7 に答える 7

11

いいえ、ビットフィールドはこの目的には適していません。レイアウトはコンパイラに依存します。

sなどの (コンパイラ固有の) 手段がない限り、結果のレイアウトを制御したいデータにビットフィールドを使用することは、通常はお勧めでき#pragmaません。

おそらく最良の方法は、ビットフィールドなしでこれを実装することです。つまり、必要なビット単位の操作を自分で行うことです。これは面倒ですが、何とかこれを修正する方法を掘り下げるよりもはるかに簡単です。また、プラットフォームに依存しません。

ヘッダーを 16 ビット ワードの配列として定義すると、チェックサムを簡単に計算できます。

于 2013-02-28T13:12:58.487 に答える
3

C11 標準は次のように述べています。

実装では、ビットフィールドを保持するのに十分な大きさのアドレス指定可能なストレージ ユニットを割り当てることができます。十分なスペースが残っている場合、構造内の別のビットフィールドの直後に続くビットフィールドは、同じユニットの隣接するビットにパックされます。十分なスペースが残っていない場合、収まらないビットフィールドを次のユニットに配置するか、隣接するユニットとオーバーラップするかは実装定義です。ユニット内のビットフィールドの割り当て順序 (上位から下位、または下位から上位) は実装定義です。

フィールド間にパディングがあり、フィールドの順序を制御できないことを意味するため、これは望ましくないことだと確信しています。それだけでなく、ネットワーク バイト オーダーに関して実装の気まぐれです。unsigned intさらに、が 16 ビットのみで、32 ビットのビットフィールドをそれに合わせるように要求している場合を想像してください。

ビットフィールドの幅を指定する式は、コロンと式が省略された場合に指定される型のオブジェクトの幅を超えない非負の値を持つ整数定数式でなければなりません。

unsigned char構造体の代わりに s の配列を使用することをお勧めします。このようにして、パディングとネットワーク バイト オーダーを確実に制御できます。構造体の合計サイズをビット単位で指定することから始めます。IP_PACKET_BITCOUNT などの定数でこれを宣言していると仮定します。typedef unsigned char ip_packet[(IP_PACKET_BITCOUNT / CHAR_BIT) + (IP_PACKET_BITCOUNT % CHAR_BIT > 0)];

void set_bits(ip_packet p, size_t bitfield_offset, size_t bitfield_width, unsigned char *value) { ... }bit から始まるp[bitfield_offset / CHAR_BIT]ビットbitfield_offset % CHARBITを、value で見つかったビットに、最大ビット長まで設定できる関数を作成しますbitfield_width。これは、タスクの最も複雑な部分です。

次に、VER_OFFSET 0 と VER_WIDTH 4、HLEN_OFFSET 4 と HLEN_WIDTH 4 などの識別子を定義して、配列の変更を簡単に行うことができます。

于 2013-02-28T16:42:54.707 に答える
1

ずいぶん前に質問されましたが、あなたの結果の説明には答えがありません。誰かの役に立てば幸いです。

データ構造の最初の 16 ビットを使用してバグを説明します。

注意: この説明は、お使いのプロセッサとコンパイラのセットでのみ正しいことが保証されています。これらのいずれかが変更されると、動作が変わる可能性があります。

田畑:

unsigned int ver   : 4;
unsigned int hlen  : 4;
unsigned int stype : 8;

に割り当てられた:

dgram.ver   = 4;
dgram.hlen  = 5;
dgram.stype = 0;

コンパイラは、オフセット 0 から始まるビット フィールドの割り当てを開始します。これは、データ構造の最初のバイトが次のようにメモリに格納されることを意味します。

Bit offset: 7     4     0
            -------------
            |  5  |  4  |
            -------------

代入後の最初の 16 ビットは次のようになります。

Bit offset:     15   12    8     4     0
                -------------------------
                |  5  |  4  |  0  |  0  |
                -------------------------
Memory Address: 100          101

Unsigned 16 ポインタを使用してメモリ アドレス 100 を逆参照しています。その結果、アドレス 100 は 16 ビット数の LSB として扱われます。また、101 は 16 ビット数の MSB として扱われます。

*ptr を 16 進数で出力すると、次のように表示されます。

*ptr = 0x0054

ループはこの 16 ビット値で実行されているため、次のようになります。

00000000 0101 0100
-------- ---- ----
   0       5    4

解決策: 要素の順序を

unsigned int hlen  : 4;
unsigned int ver   : 4;
unsigned int stype : 8;

unsigned char * ポインターを使用して、値をトラバースして出力します。それはうまくいくはずです。

他の人が言ったように、この動作はプラットフォームとコンパイラ固有のものであることに注意してください。これらの変更のいずれかが発生した場合は、データ構造のメモリ レイアウトが正しいことを確認する必要があります。

于 2016-12-23T16:24:50.483 に答える
-1

コンパイラに依存するかどうかは、非常に高速なプログラムを作成するか、異なるコンパイラで動作するプログラムが必要かによって異なります。C 用に高速でコンパクトなアプリケーションを作成するには、ビット フィールドを持つ stuct を使用します。遅い汎用プログラムが必要な場合は、長いコードにします。

于 2014-01-04T22:49:31.397 に答える
-2

unwind の言うことに同意します。ビット フィールドはコンパイラに依存します。

ビットを特定の順序にする必要がある場合は、データを文字配列へのポインターにパックします。パックされる要素のサイズだけバッファーをインクリメントします。次の要素をパックします。

pack( char** buffer )
{
   if ( buffer & *buffer )
   {
     //pack ver
     //assign first 4 bits to 4. 
     *((UInt4*) *buffer ) = 4;
     *buffer += sizeof(UInt4); 

     //assign next 4 bits to 5
     *((UInt4*) *buffer ) = 5;
     *buffer += sizeof(UInt4); 

     ... continue packing
   }
}
于 2013-02-28T15:55:59.797 に答える