我正在尝试将数据流转换为结构,因为数据流由固定宽度的消息组成,并且每条消息也具有完整定义的固定宽度字段。我计划创建一个结构,然后使用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终止符问题?
答案 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()
),只是不添加虚方法或继承,并且它将覆盖正常。