-1

我需要用这个头文件填充二进制文件。标头的校验和将添加到标头的末尾。校验和应为 1 字节总和

typedef struct {
    uint32 startAddress;
    uint16 size;
    uint32 EndAddress;
    uint8 type;
    uint8 version;
    uint16 modelNo;
    uint8 checksum;
    uint8 reserved[32];
}
4

1 回答 1

1

结构包含不同数据类型的事实并不是真正的问题 - 您可以将需要校验和的对象的地址转换为 auint8*并将其视为字节数组。

您真正的问题是编译器可以自由地以实现定义的方式对齐成员,并在成员之间插入填充以强制对齐。填充字节的值是不确定的,但校验和将包括它们,使它们在实例之间不可比较。

简单的解决方案是覆盖自然对齐并通过编译器提供的任何方式“打包”结构。这对于资源严重受限的系统有好处,但并不总是合适的。您可能会考虑使用序列化函数,其中成员被单独复制到字节数组中,然后您对其进行校验和。然而,与编译器打包相比,序列化是资源和处理器密集型的,并且可能容易出错和维护开销。

打包和序列化的替代方法可能是使用 a 为每个此类对象初始化整个结构空间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()

于 2020-05-15T20:07:19.430 回答