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

时间:2019-07-03 15:36:41

标签: c struct alignment

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

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

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

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

  1. 打包该结构(例如#pragma pack (1))。缺点:我宁愿避免打包以提高性能。
  2. 按字段计算校验和。缺点:修改结构时需要更新代码,并且需要更多代码(取决于字段数)。
  3. 将所有结构字节设置为零,然后再设置值。缺点:我不能完全保证所有结构最初都被清零。
  4. 安排结构字段以避免填充,并可能添加虚拟字段以填充填充。缺点:不是通用的,修改结构时需要仔细重新排列结构。

有没有更好的通用方法?

计算校验和示例:

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;
}

2 个答案:

答案 0 :(得分:4)

  

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

没有一个完全符合标准的程序可以依靠的机制。这来自

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

  2. 事实

      

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

    C2011, 6.2.6.1/6

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

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

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

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

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

答案 1 :(得分:2)

这听起来像是XY问题。计算内存中C对象的校验和通常不是有意义的操作。结果取决于C的实现(即使不是特定的编译器,也取决于ARCH / ABI),并且C不接受容错编程模型,该模型无法处理由于内存的硬件故障而导致对象值从您下面更改的可能性-安全错误。校验和主要适用于磁盘上或您要防止存储/传输中的数据损坏的网络上传输的序列化数据。 C结构不用于序列化(尽管它们经常被滥用)。如果编写正确的序列化例程,则只需对序列化的字节流进行校验和。