1

データストリームは固定幅メッセージで構成されており、各メッセージには固定幅フィールドも完全に定義されているため、データストリームを構造体にキャストしようとしています。構造体を作成し、reinterpret_cast を使用してデータストリームへのポインターを構造体にキャストし、フィールドを取得することを計画していました。テスト コードを作成したところ、奇妙な結果が得られました。なぜ私がこれらを取得しているのか、またはコードを修正する方法を説明できますか? (データストリームはバイナリと英数字が混在していますが、文字列でテストしているだけです)

#pragma pack(push,1)
struct Header 
{
    char msgType[1];
    char filler[1];
    char third[1];
    char fourth[1];
};
#pragma pack(pop)

int main(void)
{
    cout << sizeof(Header) << endl;

    char* data = "four";
    Header* header = reinterpret_cast<Header*>(data);
    cout << header->msgType << endl;
    cout << header ->filler << endl;
    cout << header->third << endl;
    cout << header->fourth << endl;
    return 0;
}

出てきた結果は

4
four
our
ur
r

nullターミネータが見つからないため、ourとurの4つが印刷されていると思います。null ターミネータの問題を回避するにはどうすればよいですか?

4

6 に答える 6

3

char の配列を出力し、それを null で終わる文字列と区別できるようにするには、他のoperator<<定義が必要です。

template< size_t N >
std::ostream& operator<<( std::ostream& out, char (&array)[N] ) {
     for( size_t i = 0; i != N; ++i ) out << array[i];
     return out;
}
于 2009-12-09T14:55:01.917 に答える
2

null ターミネータがないことについては正しいです。「ur」が再度表示されるのは、ヘッダー -> 4 番目ではなくヘッダー -> 3 番目を繰り返したためです。「char[1]」の代わりに、これらの変数を「char」として宣言しないのはなぜですか?

struct Header 
{
    char msgType;
    char filler;
    char third;
    char fourth;
};
于 2009-12-09T14:24:02.090 に答える
2

問題は reinterpret_cast ではなく (使用するのは非常に悪い考えですが)、構造体内のものの型にあります。'char[1]' 型ではなく、'char' 型である必要があります。

于 2009-12-09T14:26:16.013 に答える
0

私の経験では、使用#pragma packは頭痛の種を引き起こしました-部分的には正しくポップされないコンパイラが原因ですが、開発者が1つのヘッダーをポップするのを忘れていることも原因です。そのような1つの間違いと構造体は、コンパイル単位に含まれる順序ヘッダーに応じて異なる方法で定義されることになります。これはデバッグの悪夢です。

私はその理由でメモリオーバーレイを行わないようにしています-あなたの構造体があなたが期待しているデータと適切に整列しているとは信じられません。代わりに、「ネイティブ」C ++形式のメッセージからのデータを含む構造体(またはクラス)を作成します。たとえば、位置合わせの目的でそこにある場合は、「フィラー」フィールドを定義する必要はありません。そして、おそらく、フィールドのタイプの方が、フィールドのタイプよりも理にかなっていintますchar[4]。できるだけ早く、データストリームを「ネイティブ」タイプに変換してください。

于 2009-12-09T15:10:56.630 に答える
0

オーバーレイ可能な構造体を使い続けたいと仮定すると(Alexeyのコードでのコピーを回避するため、これは賢明です)、生のchar配列を次のようなラッパーに置き換えることができます。

template <int N> struct FixedStr {
    char v[N];
};

template <int N>
std::ostream& operator<<( std::ostream& out, FixedStr const &str) {
    char const *nul = (char const *)memchr(str.v, 0, N);
    int n = (nul == NULL) ? N : nul-str.v;
    out.write(str.v, n);
    return out;
}

生成された構造は次のようになります。

struct Header 
{
    FixedStr<1> msgType;
    FixedStr<1> filler;
    FixedStr<1> third;
    FixedStr<40> forty;
};

既存のコードは正常に機能するはずです。

NB。必要に応じてFixedStrにメソッドを追加できます(たとえば、std::string FixedStr::toString())仮想メソッドや継承を追加しないでください。うまくオーバーレイされます。

于 2009-12-09T15:11:31.727 に答える
0
#pragma pack(push,1)
template<int N>
struct THeader 
{
    char msgType[1+N];
    char filler[1+N];
    char third[1+N];
    char fourth[1+N];
};
typedef THeader<0> Header0;
typedef THeader<1> Header1;  
Header1 Convert(const Header0 & h0) {
   Header1  h1 = {0};
   std::copy(h0.msgType, h0.msgType + sizeof(h0.msgType)/sizeof(h0.msgType[0]), h1.msgType);
   std::copy(h0.filler, h0.filler+ sizeof(h0.filler)/sizeof(h0.filler[0]), h1.filler);
   std::copy(h0.third , h0.third + sizeof(h0.third) /sizeof(h0.third [0]), h1.third);
   std::copy(h0.fourth, h0.fourth+ sizeof(h0.fourth)/sizeof(h0.fourth[0]), h1.fourth);
   return h1;
}
#pragma pack(pop)


int main(void)
{
  cout << sizeof(Header) << endl;
  char* data = "four";
  Header0* header0 = reinterpret_cast<Header*>(data);
  Header1 header = Convert(*header0);
  cout << header.msgType << endl;
  cout << header.filler << endl;
  cout << header.third << endl;
  cout << header.fourth << endl;
  return 0;
}
于 2009-12-09T14:53:24.850 に答える