2016-04-16 17 views
1

パケットにデータをパックしようとしています。このパケットは64ビットでなければなりません。私はこれを持っています:ビットフィールドとアラインメント

typedef union { 
    uint64_t raw; 
    struct { 
    unsigned int magic : 8; 
    unsigned int parity : 1; 
    unsigned int stype : 8; 
    unsigned int sid  : 8; 
    unsigned int mlength : 31; 
    unsigned int message : 8; 
    } spacket; 
} packet_t; 

しかし、アライメントは保証されていないようです。

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

const char *number_to_binary(uint64_t x) 
{ 
    static char b[65]; 
    b[64] = '\0'; 

    uint64_t z; 
    int w = 0; 
    for (z = 1; w < 64; z <<= 1, ++w) 
    { 
     b[w] = ((x & z) == z) ? '1' : '0'; 
    } 

    return b; 
} 

int main(void) 
{ 
    packet_t ipacket; 
    bzero(&ipacket, sizeof(packet_t)); 
    ipacket.spacket.magic = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.parity = 1; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.stype = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.sid = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.mlength = 2147483647; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
    ipacket.spacket.message = 255; 
    printf("%s\n", number_to_binary(ipacket.raw)); 
} 

私は(ビッグエンディアン)を取得::私はこれを実行したときので、それは.sidフィールドの横に右でなければなりませんが、

1111111100000000000000000000000000000000000000000000000000000000 
1111111110000000000000000000000000000000000000000000000000000000 
1111111111111111100000000000000000000000000000000000000000000000 
1111111111111111111111111000000000000000000000000000000000000000 
1111111111111111111111111000000011111111111111111111111111111110 
1111111111111111111111111000000011111111111111111111111111111110 

マイ.mlengthフィールドが右側の部分のどこかに失われます。

This page「ビットフィールドを保持するアロケーションユニットのアライメント」を確認します。しかし、これが当てはまるとすれば、人々はどのようにして最初の目的であるビットフィールドにデータをパックしていますか?

フィールドが追い越される前にフィールドが取り出せる最大サイズは、24ビットであると思われます。.messageフィールドは除外されます。

+2

一般に、ビットフィールド内のデータの配置方法に依存することはできません。単語に複数のデータをパックする必要がある場合は、手動でマーシャリングを行う必要があります。 – fuz

+0

あなたの構造体は32 * 6ビットです...代わりに文字を使用する必要があります。 – xvan

+1

Cのデータ構造に特定のメモリレイアウトを使用しないでください。あまりにも多くの実装定義とコンパイラ固有のパラメータがあります。 @FUZxxlが書いたように、適切なマーシャリングを使用してください。良いコンパイラでは、これは必ずしも遅くなるわけではありません。 – Olaf

答えて

1

ビットフィールドのレイアウトに関するほとんどすべてが、SOの件に関する他の多くの質問からわかるように、標準で実装定義されています。 (とりわけ、Questions about bitfields、特にBit field's memory management in Cを見ることができます)。

あなたが64ビットにパックするためにあなたのビットフィールドをしたい場合は、あなたのコンパイラを使用して、フィールドの64ビット型を使用することができ、その後、使用することを信頼する必要があります:

typedef union { 
    uint64_t raw; 
    struct { 
    uint64_t magic : 8; 
    uint64_t parity : 1; 
    uint64_t stype : 8; 
    uint64_t sid  : 8; 
    uint64_t mlength : 31; 
    uint64_t message : 8; 
    } spacket; 
} packet_t; 

として、もともと書かれているように、現行のものに十分なスペースがない場合、ビットフィールドは新しい32ビットワードに分割されます。すなわち、magic,parity,stypeおよびsidは25ビットを占める。別の31ビットを保持するために32ビットのunsigned intが残っていないので、mlengthは次のunsigned intに格納され、そのユニットには残りの領域がありませんmessageが格納されますunsigned intユニット。それは3 * sizeof(unsigned int)または12バイトを占有する構造体を与え、uint64_tのアラインメント要件のために、ユニオンは16バイトを占有します。

標準では、私が示すものが動作することを保証するものではありません。しかし、多くのコンパイラでは、おそらく動作します。 (具体的には、Mac OS X 10.11.4のGCC 5.3.0で動作します)

+1

しかし、それでもビットフィールドが配置される順序に依存することはできません。コンパイラを変更すると、すべてが変更される可能性があります。そして、正確に言うと、ビットフィールドを新しい構造要素に分割する必要があるかどうかは、現在の構造要素に収まらない場合でも、要素にまたがる可能性があります。 –

+0

私が言ったように、それらの存在を除いて本質的にすべての実装が定義されています。 [ビットフィールドのメモリ管理](https://stackoverflow.com/questions/32677235/bit-fields-memory-management-in-c)の質問には、(YTからの)回答が標準で広範囲に引用されていますビットフィールドの主語。 –

0

アーキテクチャやコンパイラに応じて、データはさまざまなサイズに整列されます。あなたの観察から私は32ビットの整列の結果を見ていると思います。あなたがあなたのユニオンのsizeofを見て、それが8バイト(64ビット)以上である場合、データはアライメントのためにパディングされています。

32ビットの整列では、mlengthとメッセージの合計が32ビット以下になると、メッセージは互いに隣り合うことができます。これは恐らくあなたの24ビットの限界で見るものです。

32ビットアライメントで64ビットしか取らないようにするには、少し並べ替える必要があります。 1ビットのパリティは31ビットの長さの隣にあり、4つの8ビットの変数は一緒にグループ化する必要があります。

関連する問題