我需要使用此标头填充二进制文件。标头的校验和将添加到标头的末尾。校验和应为1字节总和]
typedef struct {
uint32 startAddress;
uint16 size;
uint32 EndAddress;
uint8 type;
uint8 version;
uint16 modelNo;
uint8 checksum;
uint8 reserved[32];
}
事实并非是结构包含不同的数据类型,这才是真正的问题-您可以将需要校验和的对象的地址强制转换为uint8*
并将其视为字节数组。
您的实际问题是,编译器可以自由地以实现定义的方式对齐成员,并在成员之间插入填充以强制进行对齐。填充字节的值是不确定的,但校验和将包括它们,从而使它们在实例之间不可比较。
简单的解决方案是重写自然对齐方式,并通过编译器提供的任何方式“打包”结构。这对于严重限制资源的系统是有好处的,但并不总是合适的。您可以考虑使用一个序列化函数,在该函数中将成员分别复制到一个字节数组,然后对它进行校验和。但是,与编译器打包相比,序列化需要占用大量资源和处理器,并且可能容易出错和维护开销。
打包和序列化的替代方法可能是使用memset()
初始化每个此类对象的整个结构空间,以便填充将具有已知的零值,但是如果您有多个值,则也很难维护和执行这样的对象。
因此,总而言之,您决定以任何方式打包此结构(编译器不同地支持__attribute__ or
#pragma`或什至是全局编译器选项。然后,您可能会重新考虑结构设计以从校验和和有效载荷中拆分头:
typedef struct
{
uint32 startAddress;
uint16 size;
uint32 EndAddress;
uint8 type;
uint8 version;
uint16 modelNo;
} __attribute__((__packed__)) sHeader ;
typedef struct
{
sHeader header ;
uint8 checksum;
uint8 reserved[32];
} __attribute__((__packed__)) sInfo ;
然后给出:
sInfo some_info = ... ;
然后:
info.checksum = 0 ;
for( int i = 0; i < sizeof(some_info .header); i++ )
{
info.checksum += ((uint8*)(&some_info.header))[i] ;
}
如果选择继续使用现有结构,则情况会稍微复杂一些:
typedef struct
{
uint32 startAddress;
uint16 size;
uint32 EndAddress;
uint8 type;
uint8 version;
uint16 modelNo;
uint8 checksum;
uint8 reserved[32];
} __attribute__((__packed__)) sInfo ;
sInfo some_info = ... ;
然后:
ptrdiff_t header_length = (uint8*)(&some_info.checksum) - (uint8*)(&some_info) ;
info.checksum = 0 ;
for( int i = 0; i < header_length; i++ )
{
info.checksum += ((uint8*)(&some_info.header))[i] ;
}
[第二种方法,而且更容易出错,它使用了运行时计算头的大小,而不是第一种的编译时间sizeof()
。