2013-09-06 3 views
10

私はネットワークデータストリームの解析に取り組んでいますが、データストリームを直接データ構造にマップする方法があるかどうかは疑問です。C++で構造体メンバ(ビットフィールドを含む)間のパディングを制御する方法はありますか?

たとえば、次のようにRTPプロトコルのデータ構造を定義します。

class RTPHeader 
{ 
    int version:2; // The first two bits is version. 
    int P:1; // The next bits is an field P. 
    int X:1; 
    int CC:4; 
    int M:1; 
    int PT:7; 
    int sequenceNumber; 
    int64 timestamp; 
    ..... 
}; 

このように使用します。

RTPHeader header; 
memcpy(&header, steamData, sizeof(header)); 

しかし、C++コンパイラは、メンバー間のパディングを挿入するように、パディングが(ビットフィールドメンバを含む)の部材との間に追加されないようにそれを制御する方法はありますか?

私の例ではビットフィールドがある可能性があるので、この質問はHow to get rid of padding bytes between data members of a structの複製ではありません。あなたはこれが私のためにうまく働いた

#pragma pack(push,n) // use n = 1 to have 1 Byte resolution 

typedef struct {...}MY_STRUCT; 

#pragma pack(pop) 

を使用することができますパディングバイトの挿入を回避するために

+0

私はこれを行う標準的な方法はないと思うが、あなたのコンパイラはいくつかの拡張子を持つかもしれない。 VSにはそれがあります)、あなたはパディングをコントロールすることができます。私は誰かが私を間違っていることを証明したい。 –

+0

ビットフィールドは8の倍数になります(驚くことはありません)。あなたがクラス/構造体をパックするようにコンパイラに指示するならば、あなたはOKであるはずです。 –

+3

ビットフィールド間にパディングが追加されているとは思いませんか?それはそうでなければビットフィールドのポイントを打ち負かしてしまうだろう。 –

答えて

3

このコードが任意のマシンで「機能する」必要がない限り、何バイト境界intのような制限を持っているマシンが(通常は4バイト境界)に座って、その後、

#pragma(pack) 

を使用して動作するはずです、そして、それはコンパイラの「Microsoftが互換性のあるプラグインの」Microsoft supported in GCCだけでなく、だと(このようなインテルのコンパイラとして)。

ただし、アライメントされていないアクセスはすべてのプロセッサでサポートされていないため、16ビット値の後に32ビットのintが続くブロックを開始すると問題が発生する可能性があります。

また、sequencenumberには、突然16または64ビットではなく、すべてのコンパイラで32ビットを保証するためのサイズの整数を使用します。

また、C++標準ではビットがビットフィールドに格納される順序について何も記載していないことに注意してください。間にギャップがあるかどうかは関係ありません。ビットフィールドがバイトオーダーに従って格納されることは期待できますが(リトルエンディアンマシンは最下位ビットから始めて、ビッグエンディアンマシンは最上位ビットで最初に開始します)、標準ではその点について何も述べていません。

+0

質問に答えてくれるだけでなく、ビットフィールドストアがC++とリトルエンディアンの問題でも定義されていないことを指摘しているので、この投稿を回答としてマークします。実際にはエンディアンの問題を考慮した後、私はストリームを解析するためにこのソリューションを選択しないでください。 – ZijingWu

3

また、構造体メンバのアライメント

/Zp1 

のためのコンパイルオプションを検討しかし、これはあなたのプロジェクト全体に影響を与えることになることに注意してください。

+1

これはプラットフォーム依存のコンパイラ拡張です(gccとVSが同じ構文を使用していてもわかりません)。コマンドラインオプションは間違いなくVSのみです。 –

7

C++ 11を使用できる場合は、alignof演算子で実装された整列制御を利用できます。

C++ 11コンパイラを使用できない場合は、標準以外の方法があります。 GCCでは__attribute__(packed)、MSVCでは#pragma packとなります。

あなたの選択は、GCC変異体である場合、属性は構造体の最後にを配置する必要があります:

class RTPHeader 
{ 
    int version:2; // The first two bits is version. 
    int P:1; // The next bits is an field P. 
    int X:1; 
    int CC:4; 
    int M:1; 
    int PT:7; 
    int sequenceNumber; 
    int64 timestamp; 
    ..... 
} __attribute__((packed)) ; // attribute here! 

あなたの選択はMSVC 1である場合、プラグマはを配置する必要があります構造体:

#pragma pack(1) // pragma here! 
class RTPHeader 
{ 
    int version:2; // The first two bits is version. 
    int P:1; // The next bits is an field P. 
    int X:1; 
    int CC:4; 
    int M:1; 
    int PT:7; 
    int sequenceNumber; 
    int64 timestamp; 
    ..... 
}; 

あなたのコードは、両方でコンパイルする必要がある場合、(C++ 11 alignofオペレータなし)唯一の方法は、条件付きコンパイルです:

#ifdef MSVC 
#pragma pack(1) 
#endif 
class RTPHeader 
{ 
    int version:2; // The first two bits is version. 
    int P:1; // The next bits is an field P. 
    int X:1; 
    int CC:4; 
    int M:1; 
    int PT:7; 
    int sequenceNumber; 
    int64 timestamp; 
    ..... 
#ifdef GCC 
}__attribute__((packed)); 
#else 
}; 
#endif 
関連する問題