【问题标题】:How to use table based CRC-16 code?如何使用基于表格的 CRC-16 代码?
【发布时间】:2014-04-21 08:12:24
【问题描述】:

我希望有人能够解释为什么我的 CRC-16 实现似乎无法在 Visual Studio 上正确运行?我从特定设备的手册中获得了算法,并自己编写了 int main()。当我尝试将参数传递给函数时总是出现“类型”错误,所以我怀疑格式有问题?这是代码附带的信息:

  • CRC 名称:CRC-16
  • 宽度:16 位
  • 使用的多项式:1189(十六进制)
  • 种子值:FFFF(十六进制)
  • 反射输入/输出:否
  • 异或输出:否
  • 测试字符串“123456789”的CRC:5502(十六进制)

    #include <stdio.h>
    
    unsigned int crctable[256] =
    {
    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
    0x0919, 0x1890, 0x2A0B, 0x3B82, 0x4F3D, 0x5EB4, 0x6C2F, 0x7DA6,
    0x8551, 0x94D8, 0xA643, 0xB7CA, 0xC375, 0xD2FC, 0xE067, 0xF1EE,
    0x1232, 0x03BB, 0x3120, 0x20A9, 0x5416, 0x459F, 0x7704, 0x668D,
    0x9E7A, 0x8FF3, 0xBD68, 0xACE1, 0xD85E, 0xC9D7, 0xFB4C, 0xEAC5,
    0x1B2B, 0x0AA2, 0x3839, 0x29B0, 0x5D0F, 0x4C86, 0x7E1D, 0x6F94,
    0x9763, 0x86EA, 0xB471, 0xA5F8, 0xD147, 0xC0CE, 0xF255, 0xE3DC,
    0x2464, 0x35ED, 0x0776, 0x16FF, 0x6240, 0x73C9, 0x4152, 0x50DB,
    0xA82C, 0xB9A5, 0x8B3E, 0x9AB7, 0xEE08, 0xFF81, 0xCD1A, 0xDC93,
    0x2D7D, 0x3CF4, 0x0E6F, 0x1FE6, 0x6B59, 0x7AD0, 0x484B, 0x59C2,
    0xA135, 0xB0BC, 0x8227, 0x93AE, 0xE711, 0xF698, 0xC403, 0xD58A,
    0x3656, 0x27DF, 0x1544, 0x04CD, 0x7072, 0x61FB, 0x5360, 0x42E9,
    0xBA1E, 0xAB97, 0x990C, 0x8885, 0xFC3A, 0xEDB3, 0xDF28, 0xCEA1,
    0x3F4F, 0x2EC6, 0x1C5D, 0x0DD4, 0x796B, 0x68E2, 0x5A79, 0x4BF0,
    0xB307, 0xA28E, 0x9015, 0x819C, 0xF523, 0xE4AA, 0xD631, 0xC7B8,
    0x48C8, 0x5941, 0x6BDA, 0x7A53, 0x0EEC, 0x1F65, 0x2DFE, 0x3C77,
    0xC480, 0xD509, 0xE792, 0xF61B, 0x82A4, 0x932D, 0xA1B6, 0xB03F,
    0x41D1, 0x5058, 0x62C3, 0x734A, 0x07F5, 0x167C, 0x24E7, 0x356E,
    0xCD99, 0xDC10, 0xEE8B, 0xFF02, 0x8BBD, 0x9A34, 0xA8AF, 0xB926,
    0x5AFA, 0x4B73, 0x79E8, 0x6861, 0x1CDE, 0x0D57, 0x3FCC, 0x2E45,
    0xD6B2, 0xC73B, 0xF5A0, 0xE429, 0x9096, 0x811F, 0xB384, 0xA20D,
    0x53E3, 0x426A, 0x70F1, 0x6178, 0x15C7, 0x044E, 0x36D5, 0x275C,
    0xDFAB, 0xCE22, 0xFCB9, 0xED30, 0x998F, 0x8806, 0xBA9D, 0xAB14,
    0x6CAC, 0x7D25, 0x4FBE, 0x5E37, 0x2A88, 0x3B01, 0x099A, 0x1813,
    0xE0E4, 0xF16D, 0xC3F6, 0xD27F, 0xA6C0, 0xB749, 0x85D2, 0x945B,
    0x65B5, 0x743C, 0x46A7, 0x572E, 0x2391, 0x3218, 0x0083, 0x110A,
    0xE9FD, 0xF874, 0xCAEF, 0xDB66, 0xAFD9, 0xBE50, 0x8CCB, 0x9D42,
    0x7E9E, 0x6F17, 0x5D8C, 0x4C05, 0x38BA, 0x2933, 0x1BA8, 0x0A21,
    0xF2D6, 0xE35F, 0xD1C4, 0xC04D, 0xB4F2, 0xA57B, 0x97E0, 0x8669,
    0x7787, 0x660E, 0x5495, 0x451C, 0x31A3, 0x202A, 0x12B1, 0x0338,
    0xFBCF, 0xEA46, 0xD8DD, 0xC954, 0xBDEB, 0xAC62, 0x9EF9, 0x8F70
    };
    unsigned int // Returns Calculated CRC value
    CalculateCRC16(
    unsigned int crc_seed, // Seed for CRC calculation
    void *c_ptr, // Pointer to byte array to perform CRC on
    unsigned int len) // Number of bytes to CRC
    {
    unsigned char *c = c_ptr;
    unsigned int crc = crc_seed;
    while (len--){
    crc = (crc << 8) ^ crctable[((crc >> 8) ^ *c++)];
    printf("%d", crc);
    }
    return (crc);
    }
    
    int main(){
    
        printf("%d", CalculateCRC16(0xFFFF, "123456789", 2));
    
     return 0;  
    }
    

【问题讨论】:

  • 您能告诉我们您遇到了什么错误(或警告)吗?
  • 当然,当我尝试像在本例中的 main 中那样调用它时,它确实会执行,但随后会立即中断。从调试中我发现它一退出while循环就会中断......但我不明白为什么。
  • 出现错误如何调试?你是什​​么意思,“休息”。请具体说明您的问题并提供完整的详细信息。
  • 缩进,缩进,缩进……
  • 你的crc变量是一个unsigned int,所以当你左移它时,你会保留超过16位,当你试图索引你的256个元素的查找表时,这会导致悲伤。将其更改为 uint16_t (#include &lt;stdint.h&gt;),您应该可以开始了。

标签: c crc16


【解决方案1】:

该表列出了多项式的幂,避免表中的错误在计算过程中计算幂,并使多项式成为一个入口以及初始值。使用 Dartpad 模拟:https://dartpad.dev/?null_safety=true

void main() {
  final int polynomial = 0x1189;
  final int initialValue = 0xFFFF;
  String crc16(String expression, int polynomial, int initialValue) {
    int checkSum = initialValue;
    for (int i = 0; i < expression.length; i++) {
      checkSum ^= (expression.codeUnitAt(i) << 8);
      for (int j = 0; j < 8; j++) {
        if (((checkSum <<= 1) & 0x10000) == 0x10000) checkSum ^= polynomial;
        checkSum &= 0xFFFF;
      }
    }
    return checkSum.toRadixString(16).toUpperCase();
  }
  print(crc16('123456789', polynomial, initialValue));
}

要符合 CRC16 CCITT,您应该使用多项式 = 0x1021 和初始值 = 0xFFFF。

https://www.gatevidyalay.com/cyclic-redundancy-check-crc-error-detection/ 对 CRC 有很好的解释。

【讨论】:

    【解决方案2】:

    我使用了相同的 CRC 检查,在您的代码中您只需更改以下行: crc = (crc >> 8) ^ crctable[(crc^(*c++))&0xFF];

    【讨论】:

      【解决方案3】:

      您使用的表格似乎生成不正确。前十六个条目是正确的,但之后就出现了分歧。我认为用它解码 CCITT CRC-16 不会有太大的运气。

      有关正确生成的表的示例,请参阅 CRC for cross platform applications

      【讨论】:

        【解决方案4】:

        在这条线上

        crc = (crc << 8) ^ crctable[((crc >> 8) ^ *c++)];
        

        ((crc &gt;&gt; 8) ^ *c++) 的结果可能变为 > 255,并导致访问冲突。只需通过将索引屏蔽为 crctable 来确保结果始终为 0.255

        crc = (crc << 8) ^ crctable[((crc >> 8) ^ *c++) & 0x00FF];
        // but you will need to clean up the result as its upper 16 bits
        // are not guaranteed to be all 0
        return crc & 0xffff;
        

        或者更好的是,生成的 crc 值,因为它确保 crc 永远不会包含值 > 0x0000ffff

        crc = ( (crc << 8) ^ crctable[((crc >> 8) ^ *c++)] ) & 0xffff;
        

        你就准备好了。问题的根源在于原始代码似乎是 16 位代码(其中 int 是 16 位),而您使用的是 32 位编译器。

        【讨论】:

        • 嗯,有道理。 16 位代码在当时只是司空见惯,现在已经被现代 32 位取代了吗?
        • 是的,在 DOS 时代的大部分时间里,所有 DOS 代码都是 16 位的(第一个支持 386 的 PC 处理器是 386),就在接近尾声时,有“DOS 扩展器”允许 32 位代码在 DOS 下运行。所有 pre-Warp OS/2 版本也是 16 位,如 Windows pre 95(或者可能是 3.11)。在 ARM 架构 32 位嵌入式处理器大规模普及之前,许多高端嵌入式处理器/控制器也是 16 位的,但它们仍然比较常见。
        • 另外:@pat 的解决方案更适合现代编译器,如果您仍需要使用旧编译器编译相同的代码库,您会注意到它可能不知道 uint16_t。可以使用 typedef 或 masking 来解决,就像我提出的代码一样。
        • 你的假设是错误的。 crc 的值是在函数中分配的。任何时候都不会分配超过 16 位的值。它在计算之前永远不会大于 0xFFFF。 0xFFFF&gt;&gt;8 = 0x00FF*c 引用一个字节指针并且是 8 位宽。 XOR 操作不携带任何位到其他位位置。所以(( crc &gt;&gt; 8) ^ *c)的最大值是0xFF
        • @harper。罪魁祸首是 crc uint16_t 仍然是最简单的解决方案。
        【解决方案5】:

        全部清理完毕,准备出发:

        #include <stdio.h>
        #include <stdint.h>
        
        static const uint16_t crctable[256] =
        {
            0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
            0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
            0x0919, 0x1890, 0x2A0B, 0x3B82, 0x4F3D, 0x5EB4, 0x6C2F, 0x7DA6,
            0x8551, 0x94D8, 0xA643, 0xB7CA, 0xC375, 0xD2FC, 0xE067, 0xF1EE,
            0x1232, 0x03BB, 0x3120, 0x20A9, 0x5416, 0x459F, 0x7704, 0x668D,
            0x9E7A, 0x8FF3, 0xBD68, 0xACE1, 0xD85E, 0xC9D7, 0xFB4C, 0xEAC5,
            0x1B2B, 0x0AA2, 0x3839, 0x29B0, 0x5D0F, 0x4C86, 0x7E1D, 0x6F94,
            0x9763, 0x86EA, 0xB471, 0xA5F8, 0xD147, 0xC0CE, 0xF255, 0xE3DC,
            0x2464, 0x35ED, 0x0776, 0x16FF, 0x6240, 0x73C9, 0x4152, 0x50DB,
            0xA82C, 0xB9A5, 0x8B3E, 0x9AB7, 0xEE08, 0xFF81, 0xCD1A, 0xDC93,
            0x2D7D, 0x3CF4, 0x0E6F, 0x1FE6, 0x6B59, 0x7AD0, 0x484B, 0x59C2,
            0xA135, 0xB0BC, 0x8227, 0x93AE, 0xE711, 0xF698, 0xC403, 0xD58A,
            0x3656, 0x27DF, 0x1544, 0x04CD, 0x7072, 0x61FB, 0x5360, 0x42E9,
            0xBA1E, 0xAB97, 0x990C, 0x8885, 0xFC3A, 0xEDB3, 0xDF28, 0xCEA1,
            0x3F4F, 0x2EC6, 0x1C5D, 0x0DD4, 0x796B, 0x68E2, 0x5A79, 0x4BF0,
            0xB307, 0xA28E, 0x9015, 0x819C, 0xF523, 0xE4AA, 0xD631, 0xC7B8,
            0x48C8, 0x5941, 0x6BDA, 0x7A53, 0x0EEC, 0x1F65, 0x2DFE, 0x3C77,
            0xC480, 0xD509, 0xE792, 0xF61B, 0x82A4, 0x932D, 0xA1B6, 0xB03F,
            0x41D1, 0x5058, 0x62C3, 0x734A, 0x07F5, 0x167C, 0x24E7, 0x356E,
            0xCD99, 0xDC10, 0xEE8B, 0xFF02, 0x8BBD, 0x9A34, 0xA8AF, 0xB926,
            0x5AFA, 0x4B73, 0x79E8, 0x6861, 0x1CDE, 0x0D57, 0x3FCC, 0x2E45,
            0xD6B2, 0xC73B, 0xF5A0, 0xE429, 0x9096, 0x811F, 0xB384, 0xA20D,
            0x53E3, 0x426A, 0x70F1, 0x6178, 0x15C7, 0x044E, 0x36D5, 0x275C,
            0xDFAB, 0xCE22, 0xFCB9, 0xED30, 0x998F, 0x8806, 0xBA9D, 0xAB14,
            0x6CAC, 0x7D25, 0x4FBE, 0x5E37, 0x2A88, 0x3B01, 0x099A, 0x1813,
            0xE0E4, 0xF16D, 0xC3F6, 0xD27F, 0xA6C0, 0xB749, 0x85D2, 0x945B,
            0x65B5, 0x743C, 0x46A7, 0x572E, 0x2391, 0x3218, 0x0083, 0x110A,
            0xE9FD, 0xF874, 0xCAEF, 0xDB66, 0xAFD9, 0xBE50, 0x8CCB, 0x9D42,
            0x7E9E, 0x6F17, 0x5D8C, 0x4C05, 0x38BA, 0x2933, 0x1BA8, 0x0A21,
            0xF2D6, 0xE35F, 0xD1C4, 0xC04D, 0xB4F2, 0xA57B, 0x97E0, 0x8669,
            0x7787, 0x660E, 0x5495, 0x451C, 0x31A3, 0x202A, 0x12B1, 0x0338,
            0xFBCF, 0xEA46, 0xD8DD, 0xC954, 0xBDEB, 0xAC62, 0x9EF9, 0x8F70
        };
        
        uint16_t // Returns Calculated CRC value
        CalculateCRC16(
            uint16_t crc,      // Seed for CRC calculation
            const void *c_ptr, // Pointer to byte array to perform CRC on
            size_t len)        // Number of bytes to CRC
        {
            const uint8_t *c = c_ptr;
        
            while (len--)
                crc = (crc << 8) ^ crctable[((crc >> 8) ^ *c++)];
        
            return crc;
        }
        
        int main()
        {
            printf("%04x\n", CalculateCRC16(0xFFFF, "123456789", 9));
        
            return 0;
        }
        

        【讨论】:

        • 谢谢帕特,这是一个巨大的改进。但是,对于测试字符串,它似乎没有给出结果 5502。我得到的是 7A2C。你认为类型变化可能影响了算法吗?
        • 你是如何计算期望值的?请注意,您只获取测试字符串前 2 个字节的 CRC。
        • 将长度从 2 改为 9,得到你期望的值
        【解决方案6】:

        现在我附近没有电脑。试一试:

        crc = (crc > 8) ^ *c++];

        【讨论】:

        • 因为 0 ^ 0 等于 0,所以不需要强制转换。我认为 crc 应该是“无符号短整数”。 pat 是对的,uint16_t 是最好的选择。
        • 更新:由于 0 ^ 0 等于 0,如果 crc 为 16bits,则不需要强制转换。对于错误,crc似乎超过16位,所以我认为您可以使用“unsigned short int”来定义crc。而@pat 是对的,uint16_t 是最好的选择。
        猜你喜欢
        • 2018-02-21
        • 2018-10-01
        • 1970-01-01
        • 2014-05-16
        • 2021-08-09
        • 1970-01-01
        • 2020-03-19
        • 2021-06-10
        • 1970-01-01
        相关资源
        最近更新 更多