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

我认为四个,我们的和你的正在打印,因为它找不到空终止符。如何解决空终止符问题?

4

6 回答 6

3

为了能够打印字符数组,并能够将其与以空字符结尾的字符串区分开来,您需要其他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

你说的没有空终止符是对的。它再次打印“ur”的原因是因为您重复了 header->third 而不是 header->fourth。为什么不将这些变量声明为“char”,而不是“char[1]”?

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

问题不在于 reinterpret_cast (尽管使用它是一个非常糟糕的主意),而是结构中事物的类型。它们应该是“char”类型,而不是“char[1]”类型。

于 2009-12-09T14:26:16.013 回答
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 回答
0

以我的经验,使用#pragma pack已经引起了头疼——部分原因是编译器没有正确弹出,还因为开发人员忘记弹出一个标题。像这样的一个错误和结构最终定义不同,具体取决于编译单元中包含哪些订单头。这是调试的噩梦。

出于这个原因,我尽量不做内存覆盖——你不能相信你的结构与你期望的数据正确对齐。相反,我创建了包含来自“本机”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;
};

并且您现有的代码应该可以正常工作。

注意。如果需要(例如,std::string FixedStr::toString()),您可以向 FixedStr 添加方法,但不要添加虚拟方法或继承,它会很好地覆盖。

于 2009-12-09T15:11:31.727 回答