使用reinterpret_cast<>的问题在c ++中

时间:2009-12-09 14:21:08

标签: c++ pragma reinterpret-cast

我正在尝试将数据流转换为结构,因为数据流由固定宽度的消息组成,并且每条消息也具有完整定义的固定宽度字段。我计划创建一个结构,然后使用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终止符问题?

6 个答案:

答案 0 :(得分: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;
}

答案 1 :(得分:2)

你对缺少null终结符是正确的。它再次打印“ur”的原因是因为你重复了header-&gt; third而不是header-&gt; 4th。而不是“char [1]”,为什么不将这些变量声明为“char”?

struct Header 
{
    char msgType;
    char filler;
    char third;
    char fourth;
};

答案 2 :(得分:2)

问题不在于reinterpret_cast(虽然使用它是一个非常糟糕的主意),但在结构中的东西类型。它们应该是'char'类型,而不是'char [1]'类型。

答案 3 :(得分: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;
}

答案 4 :(得分:0)

根据我的经验,使用#pragma pack引起了令人头疼的问题 - 部分原因是编译器没有正确弹出,但也是由于开发人员忘记弹出一个标题。这样的一个错误和结构最终定义不同,具体取决于哪个 order 标头包含在编译单元中。这是调试的噩梦。

由于这个原因,我尽量不做内存叠加 - 你不能相信你的结构与你期望的数据正确对齐。相反,我创建了包含来自“本机”C ++格式的消息中的数据的结构(或类)。例如,如果仅用于对齐目的,则不需要定义“填充”字段。也许对于int字段的类型而言,它比char[4]更有意义。请尽快将数据流转换为“本机”类型。

答案 5 :(得分: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()),只是不添加虚方法或继承,并且它将覆盖正常。