温馨提示:本文翻译自stackoverflow.com,查看原文请点击:c - Skip/avoid alignment padding bytes when calculating struct checksum
c struct alignment

c - 计算结构校验和时跳过/避免对齐填充字节

发布于 2020-03-27 12:03:04

在计算C结构的校验和时,是否有通用的方法跳过/避免对齐填充字节?

我想通过对字节求和来计算结构的校验和。问题是,该结构具有对齐填充字节,它们可以获取随机(未指定)值,并使具有相同数据的两个结构获得不同的校验和值。

注意:我主要关心的是可维护性(无需更新代码即可添加/删除/修改字段)和可重用性,而不是可移植性(该平台非常具体且不太可能更改)。

目前,我找到了一些解决方案,但是它们都有缺点:

  1. Pack the struct (e.g. #pragma pack (1)). Disadvantage: I prefer to avoid packing for better performance.
  2. Calculate checksum field by field. Disadvantage: The code will need to be updated when modifying the struct and requires more code (depending on the number of fields).
  3. Set to zero all struct bytes before setting values. Disadvantage: I cannot fully guarantee that all structs were initially zeroed.
  4. Arrange the struct fields to avoid padding and possibly add dummy fields to fill padding. Disadvantage: Not generic, the struct will need to be carefully rearranged when modifying the struct.

Is there a better generic way?

Calculating checksum example:

unsigned int calcCheckSum(MyStruct* myStruct)
{
    unsigned int checkSum = 0; 
    unsigned char* bytes = (unsigned char*)myStruct;
    unsigned int byteCount = sizeof(MyStruct);
    for(int i = 0; i < byteCount; i++)
    {
        checkSum += bytes[i];
    }
    return checkSum;
}

查看更多

查看更多

提问者
Eliahu Aaron
被浏览
198
John Bollinger 2019-07-04 01:39

在计算C结构的校验和时,是否有通用的方法跳过/避免对齐填充字节?

尚无严格遵循的程序可以依靠的机制。这是从

  1. 允许C实现出于任何原因或没有原因而在任何一个或多个成员之后使用任意填充对结构进行布局的事实,并且

  2. 事实

    当将值存储在结构或联合类型的对象中(包括在成员对象中)时,与任何填充字节对应的对象表示形式的字节采用未指定的值。

    C2011,6.2.6.1/6

前者意味着标准没有提供保证结构布局不包含填充的一致方法,而后者则意味着从原则上讲,您无法做任何操作来控制填充字节的值-即使您最初将零填充为零,填充结构实例后,只要您分配给该对象或其任何成员,任何填充都将使用不确定的值。

在实践中,您在问题中提到的任何方法都可能在C实现和数据性质允许的范围内发挥作用。但是只有(2)可以逐个成员地计算校验和,才可以由严格遵循的程序使用,而且正如我所指的那样,该程序不是“泛型的”。 这就是我会选择的如果您有许多需要校验和的独特结构,那么可能值得部署代码生成器或宏魔术来帮助您进行维护。

另一方面,提供通用校验和的最可靠方法是行使特定于实现的扩展,该扩展使您能够避免包含任何填充的结构(您的(1))。请注意,这会将您与特定的C实现或兼容实现这种扩展的实现联系在一起,使其可能在某些系统上根本无法工作(例如,那些未对齐访问是硬错误的系统),并且可能会降低性能。其他系统。

您的(4)是避免填充的另一种方法,但这将是可移植性和维护的噩梦。不过,从某种意义上讲,校验和算法不需要关注单个成员,它可以提供通用的校验和。但也请注意,这也对类似于(3)的初始化行为提出了要求。那会便宜些,但不会完全自动。

实际上,C实现并不一定要修改填充字节,但也不一定要保留它们。特别是,即使您严格按照零(3)进行零填充,也不能保证通过整个结构分配或当您按值传递或返回结构时都可以复制填充。如果要执行任何这些操作,则需要在接收端采取措施以确保零填充,并需要逐个成员注意。