我目前正在处理一些二进制文件,数据格式在某些文件中指定。我使用的方法是定义一些相应的结构,以便从缓冲区中逐个读取数据。例如,如果我知道在文件的开头,有一个数据包标头指定了该数据包中的以下数据类型和数据长度,我将首先解析此数据包标头。
#pragma pack (1)
struct PacketHeader
{
uint16_t PacketSize;
uint16_t PacketType;
};
char *buffer = new char[size];
file.read(buffer, size);
//read in the PacketHeader
PacketHeader ph;
ph = *(PacketHeader *)buffer;
//switch data type
switch(ph.PacketType)
{
//do something
}
到目前为止,一切顺利,但是当我不使用结构方法时会出现问题。例如,我知道在数据类型A
之后缓冲区的某个位置,会有一些关于A
底层组成的信息,比如一个uint32_t
变量,另一个uint32_t
变量。这种变量对的数量在 A
中指定,并且由于该对中只有两个变量,我尝试直接解析它们而没有任何结构,例如,
//get pair_num from previous parsed data
for(int i = 0; i < pair_num; i++)
{
std::cout << (uint32_t)(buffer + 2 * i * sizeof(uint32_t))
<< (uint32_t)(buffer + (2 * i + 1) * sizeof(uint32_t))
<< std::endl;
}
但是,上面的代码不起作用。从文件中解析的两个变量是错误的。所以我回到结构方法,并设法使用以下代码获得正确的结果:
struct B
{
uint32_t v1;
uint32_t v2;
}
B b;
//get pair_num from previous parsed data
for(int i = 0; i < pair_num; i++)
{
b = *(B *)(buffer + i * 8);
std::cout << b.v1
<< b.v2
<< std::endl;
}
我只是想知道这两种方法有什么区别?有人能给我一些见解吗?
不是将char *
转换为uint32_t *
并取消引用它,而是char *
将指针转换为uint32_t
以便在std::cout
上打印地址(或 64 位机器上的一部分地址(,正确的方法应该是这样的:
for(int i = 0; i < pair_num; i++)
{
std::cout << *((uint32_t *)(buffer + 2 * i * sizeof(uint32_t)))
<< *((uint32_t *)(buffer + (2 * i + 1) * sizeof(uint32_t)))
<< std::endl;
}
但更简单的方法是使用适当的指针:
uint32_t *ptr = (uint32_t *) buffer;
for(int i = 0; i < pair_num; i++)
{
std::cout << *(ptr + 2 * i )
<< *(ptr + 2 * i + 1 )
<< std::endl;
}
甚至:
uint32_t *ptr = (uint32_t *) buffer;
for(int i = 0; i < pair_num; i++)
{
std::cout << ptr[2 * i]
<< ptr[2 * i + 1]
<< std::endl;
}
请注意,在第二种方式(以及使用标题作为结构(中,您正在执行不必要的复制,我认为您想要的是:
struct B
{
uint32_t v1;
uint32_t v2;
}
//get pair_num from previous parsed data
for(int i = 0; i < pair_num; i++)
{
B *b = (B *)(buffer + i * 8);
std::cout << b->v1
<< b->v2
<< std::endl;
}
除非您出于某种原因有意复制该结构。