我通过网络传输了一个字符/字节数组。它包含标题和一些数据。我想将标题映射到结构上。这是一个例子:
#pragma pack(1)
struct Header
{
unsigned short bodyLength;
int msgID;
unsigned short someOtherValue;
unsigned short protocolVersion;
};
int main()
{
boost::array<char, 128> msgBuffer;
Header header;
for(int x = 0; x < sizeof(Header); x++)
msgBuffer[x] = 0x01; // assign some values
memcpy(&header, msgBuffer.data(), sizeof(Header));
system("PAUSE");
return 0;
}
假设结构永远不包含任何可变长度字段,这总是有效吗?是否有独立/惯用的平台方式?
注意:
我在互联网上看到了很多允许你序列化/反序列化的库,但我得到的印象是,如果以前用同一个库序列化它们,它们只能反序列化。好吧,我无法控制传输的格式。我肯定会得到一个byte / char数组,其中所有的值都是相互跟随的。
答案 0 :(得分:5)
某些处理器要求某些类型正确对齐。他们不接受指定的包装并生成硬件陷阱。
即使在常见的x86压缩结构上也会导致代码运行得更慢。
使用不同的字节顺序平台时,您也必须小心。
顺便说一下,如果你想要一个简单且独立于平台的通信机制,并且绑定到许多编程语言,那么看看YAMI。
答案 1 :(得分:5)
只是简单的复制很可能会破坏,至少如果数据来自不同的架构(甚至只是编译器)而不是你所使用的。这是出于以下原因:
第二个链接是特定于GCC的,但这适用于所有编译器。
我建议逐字节读取字段,并从这些字节组装更大的字段(整数等)。这使您可以控制字节顺序和填充。
答案 2 :(得分:2)
#pragma pack(1)
指令应该适用于大多数编译器,但你可以通过计算你的数据结构应该有多大来检查(如果我的数学是正确的话,你的情况是10)并使用printf("%d", sizeof(Header));
来检查包装正在进行中。
正如其他人所说,如果你在架构之间走路,你仍然需要警惕Endianness。
答案 3 :(得分:1)
我强烈反对逐字节读取的想法。如果您在结构声明中处理结构打包,则可以毫无问题地复制到结构中。对于endiannes问题,再次逐字节读取解决了问题,但没有给出通用的解决方案。那种方法非常蹩脚。我之前做过类似的工作,并且工作正常,没有任何故障。
想一想。我有一个结构,我也有相应的结构定义。您可以手动构建它,但我已经为此编写了一个解析器,并将其用于其他事情。
例如,您在上面给出的结构的定义是“s s s”。 (s = short,i = int)然后我将这个结构的结构地址,这个定义和结构包装选项提供给一个处理endiannes事物的特殊函数,并且它完成了。
SwitchEndianToBig(&amp; header,“s s s”,4); // 4 =结构包装选项
答案 4 :(得分:1)
告诉我,如果我错了,但AFAIK,这样做会保证数据是正确的 - 假设类型在不同平台上具有相同的大小:
#include <array>
#include <algorithm>
//#pragma pack(1) // not needed
struct Header
{
unsigned short bodyLength;
int msgID;
unsigned short someOtherValue;
unsigned short protocolVersion;
float testFloat;
Header() : bodyLength(42), msgID(34), someOtherValue(66), protocolVersion(69), testFloat( 3.14f ) {}
};
int main()
{
std::tr1::array<char, 128> msgBuffer;
Header header;
const char* rawData = reinterpret_cast< const char* >( &header );
std::copy( rawData, rawData + sizeof(Header), msgBuffer.data()); // assuming msgBuffer is always big enough
system("PAUSE");
return 0;
}
如果目标平台上的类型不同,则必须为每种类型使用别名(typedef),以确保每种类型的大小相同。
答案 5 :(得分:0)
我知道我与谁沟通,所以我真的不必担心字节序。但我还是希望远离编译器特定的命令。
那怎么样:
const int kHeaderSizeInBytes = 6;
struct Header
{
unsigned short bodyLength;
unsigned short msgID;
unsigned short protocolVersion;
unsigned short convertUnsignedShort(char inputArray[sizeof(unsigned short)])
{return (((unsigned char) (inputArray[0])) << 8) + (unsigned char)(inputArray[1]);}
void operator<<(char inputArray[kHeaderSizeInBytes])
{
bodyLength = convertUnsignedShort(inputArray);
msgID = convertUnsignedShort(inputArray + sizeof(bodyLength));
protocolVersion = convertUnsignedShort(inputArray + sizeof(bodyLength) + sizeof(msgID));
}
};
int main()
{
boost::array<char, 128> msgBuffer;
Header header;
for(int x = 0; x < kHeaderSizeInBytes; x++)
msgBuffer[x] = x;
header << msgBuffer.data();
system("PAUSE");
return 0;
}
摆脱了pragma,但它并不像我想的那样通用。每次向标题添加字段时,都必须修改&lt;&lt;功能。你能以某种方式迭代结构域,得到字段的类型并调用相应的函数吗?