CRC校验详解(附代码示例)

2023-11-09

目录

1.CRC校验原理

2.生成多项式

3.以CRC-16校验为例讲解编程实现

3.3.1 完全按照CRC原理实现校验

3.3.2 工程中常用CRC校验过程

3.3.3 改进的CRC校验过程

4.以CRC-8校验为例讲解查表法

5.以CRC-16校验为例讲解查表法

5.1.生成表格

5.2.查表法实现

6.代码链接


CRC校验即循环冗余校验(Cyclic Redundancy Check),是基于数据计算一组效验码,用于核对数据传输过程中是否被更改或传输错误。首先看两个概念,后续会用到。

  • 模2除法:也叫模2运算,就是结果除以2后取余数。模2除法每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在CRC计算中有应用到模2除法。
  • 多项式与二进制:二进制可表示成多项式的形式,比如二进制1101表示为: x3+x2+x0;1011表示为:x3+x1+x0。

1.CRC校验原理

CRC校验本质上是选取一个合适的除数,要进行校验的数据是被除数,然后做模2除法,得到的余数就是CRC校验值。

下面用具体的例子做讲解:给定一组数据A:10110011(二进制),选取除数B:11001。

  1. 首先需要在被除数A后加4个比特位0(具体加几个0是根据除数B的位数决定的,比如这里B是5位,那么A后面加4个0;如果B选6位,则A后面加5个0,总之加的0的个数比除数B的个数少1位。后面还会提到怎么添加)。
  2. 进行模2除法运算。注意每次都是模2运算,即异或。
  3. 最后得到余数C就是CRC校验值。注意余数位数必须比除数少1位,如果不够前面加0补齐。运算如下图所示

                   

2.生成多项式

第1章讲解了CRC校验的基本原理,通常我们把选取的除数称之为“生成多项式”。事实上,生成多项式的选取是由一定标准的,如果选的不好,那么检出错误的概率就会低很多。好在这个问题已经被专家们研究了很长一段时间了,对于我们这些使用者来说,只要把现成的成果拿来用就行了。下表是一些标准的CRC生成多项式,可以直接使用。

标准CRC生成多项式
名称 生成多项式 简记式
CRC-4 x4+x+1 0x03
CRC-8 x8+x5+x4+1 0x31
CRC-8 x8+x2+x1+1  0x07
CRC-8 x8+x6+x4+x3+x2+x1 0x5E
CRC-12 x12+x11+x3+x+1 0x080F
CRC-16 x16+x15+x2+1  0x8005
CRC16-CCITT x16+x12+x5+1 0x1021
CRC-32 x32+x26+x23+...+x2+x+1 0x04C11DB7

更多标准CRC生成式请参考https://en.wikipedia.org/wiki/Cyclic_redundancy_check

有一点要特别注意,文献中提到的生成多项式经常会说到多项式的位宽(Width,简记为W),这个位宽不是多项式对应的二进制数的位数,而是位数减1。比如CRC8中用到的位宽为8的生成多项式,其实对应得二进制数有九位:100110001。另外一点,多项式表示和二进制表示都很繁琐,交流起来不方便,因此,文献中多用16进制简写法来表示,因为生成多项式的最高位肯定为1,最高位的位置由位宽可知,故在简记式中,将最高的1统一去掉了,如CRC32的生成多项式简记为04C11DB7实际上表示的是104C11DB7。当然,这样简记除了方便外,在编程计算时也有它的用处。所以在第一章中提到的在被除数后增加0的位数就是位宽,计算出的CRC校验值长度也是位宽。

3.以CRC-16校验为例讲解编程实现

3.3.1 完全按照CRC原理实现校验

实际工程中多使用CRC-16校验,即选取生成多项式为0x8005。按照前面提到的CRC校验原理,编程实现步骤如下:(注意实际编程时并不用这种直接的方法,如不想看可直接跳到3.3.2

  1. 预置1个16位的变量为CRC,此值存放CRC校验值,赋初值为0;
  2. 将需要校验的字符串str后面添加16个0;
  3. 如果变量CRC最高位为1,变量CRC与0x8005异或,然后将变量CRC左移1位,最低位补入1比特新的数据(来自需要校验的字符串str);
  4. 如果变量CRC最高位为0,直接将变量CRC左移1位,最低位补入1比特新的数据(来自需要校验的字符串str);
  5. 重复2-3步,直到字符串str最后1位补入变量CRC中;
  6. 此时得到的余数就是CRC校验值。

这种直接的方法有一个弊端,那就是在字符串前面加0,并不影响校验值,这就不符合我们的预期了。比如,我们想校验的1字节1001 1100,现在在前面补1字节0,变成2字节0000 0000 1001 1100,结果两个得到的校验值是一样的。所以在实际应用中,CRC校验过程做了一些改变:增加了“余数初始值”、“结果异或值”、“输入数据反转”和“输出数据反转”四个概念。

3.3.2 工程中常用CRC校验过程

  • 余数初始值:即在计算开始前,先给变量CRC赋的初值。
  • 结果异或值:即在计算结束后,得到的变量CRC与这个值进行异或操作,就得到了最终的校验值。
  • 输入数据反转:即在计算开始前,将需要校验的数据反转,如数据位1011,反转后为1101。
  • 输出数据反转:即在计算结束后,与结果异或值异或之前,计算值反转,如计算结果为1011,反转后为1101。

实际应用中,生成多项式、余数初始值、结果异或值、输入数据反转和输出数据反转是有对应关系的,这些对应关系是大家都遵守的标准,如下表所示:

表3-1 常见CRC参数模型
CRC算法名称 多项式公式 宽度 多项式(16进制) 初始值(16进制) 结果异或值(16进制) 输入值反转 输出值反转
CRC-4/ITU x4 + x + 1 4 03 00 00 true true
CRC-5/EPC x4 + x3 + 1 5 09 09 00 false false
CRC-5/ITU x5 + x4 + x2 + 1 5 15 00 00 true true
CRC-5/USB x5 + x2 + 1 5 05 1F 1F true true
CRC-6/ITU x6 + x + 1 6 03 00 00 true true
CRC-7/MMC x7 + x3 + 1 7 09 00 00 false false
CRC-8 x8 + x2 + x + 1 8 07 00 00 false false
CRC-8/ITU x8 + x2 + x + 1 8 07 00 55 false false
CRC-8/ROHC x8 + x2 + x + 1 8 07 FF 00 true true
CRC-8/MAXIM x8 + x5 + x4 + 1 8 31 00 00 true true
CRC-16/IBM x16 + x15 + x2 + 1 16 8005 0000 0000 true true
CRC-16/MAXIM x16 + x15 + x2 + 1 16 8005 0000 FFFF true true
CRC-16/USB x16 + x15 + x2 + 1 16 8005 FFFF FFFF true true
CRC-16/MODBUS x16 + x15 + x2 + 1 16 8005 FFFF 0000 true true
CRC-16/CCITT x16 + x12 + x5 + 1 16 1021 0000 0000 true true
CRC-16/CCITT-FALSE x16 + x12 + x5 + 1 16 1021 FFFF 0000 false false
CRC-16/x5 x16 + x12 + x5 + 1 16 1021 FFFF FFFF true true
CRC-16/XMODEM x16 + x12 + x5 + 1 16 1021 0000 0000 false false
CRC-16/DNP x16 + x13 + x12 + x11 + x10 + x8 + x6 + x5 + x2 + 1 16 3D65 0000 FFFF true true
CRC-32 x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 32 04C11DB7 FFFFFFFF FFFFFFFF true true
CRC-32/MPEG-2 x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1 32 04C11DB7 FFFFFFFF 00000000 false false

接下来以CRC-16/IBM校验为例,讲解工程中使用的CRC校验编程实现。具体实现时,以字节为单位进行计算。

  1. 预置1个16位的变量CRC,存放校验值,首先根据表3-1赋初值0x0000;
  2. 将第1个字节按照表3-1看是否需要反转,若需要,则按反转,若不需要,直接进入第3步。这里需要反转;
  3. 把第1个字节按照步骤2处理后,与16位的变量CRC的高8位相异或,把结果放于变量CRC,低8位数据不变;
  4. 把变量CRC的内容左移1位(朝高位)用0填补最低位,并检查左移后的移出位;
  5. 如果移出位为0:重复第4步(再次左移一位);如果移出位为1,变量CRC与多项式8005(1000 0000 0000 0101)进行异或;
  6. 重复步骤4和5,直到左移8次,这样整个8位数据全部进行了处理;
  7. 重复步骤2到步骤6,进行通讯信息帧下一个字节的处理;
  8. 将该通讯信息帧所有字节按上述步骤计算完成后,将得到的16位变量CRC按照表3-1看是否需要反转,这里需要反转;
  9. 最后,与结果异或值异或,得到的变量CRC即为CRC校验值;

我在这里按照如上方法整理了一个通用代码,包含CRC-8,CRC-16和CRC-32。代码如下:

type.h

#ifndef __TYPE_H__
#define __TYPE_H__
#include <stdio.h>

typedef unsigned char        u8;
typedef unsigned short       u16;
typedef unsigned int         u32;
typedef unsigned long long   u64;

typedef unsigned char        BOOL;
#define FALSE                0
#define TRUE                 1

#endif

crc.h文件

#ifndef __CRC_H__
#define __CRC_H__
#include "type.h"

typedef struct
{
	u8 poly;//多项式
	u8 InitValue;//初始值
	u8 xor;//结果异或值
	BOOL InputReverse;
	BOOL OutputReverse;
}CRC_8;

typedef struct
{
	u16 poly;//多项式
	u16 InitValue;//初始值
	u16 xor;//结果异或值
	BOOL InputReverse;
	BOOL OutputReverse;
}CRC_16;

typedef struct
{
	u32 poly;//多项式
	u32 InitValue;//初始值
	u32 xor;//结果异或值
	BOOL InputReverse;
	BOOL OutputReverse;
}CRC_32;

const CRC_8 crc_8;
const CRC_8 crc_8_ITU;
const CRC_8 crc_8_ROHC;
const CRC_8 crc_8_MAXIM;

const CRC_16 crc_16_IBM;
const CRC_16 crc_16_MAXIM;
const CRC_16 crc_16_USB;
const CRC_16 crc_16_MODBUS;
const CRC_16 crc_16_CCITT;
const CRC_16 crc_16_CCITT_FALSE;
const CRC_16 crc_16_X5;
const CRC_16 crc_16_XMODEM;
const CRC_16 crc_16_DNP;

const CRC_32 crc_32;
const CRC_32 crc_32_MPEG2;

u8 crc8(u8 *addr, int num,CRC_8 type) ;
u16 crc16(u8 *addr, int num,CRC_16 type) ;
u32 crc32(u8 *addr, int num,CRC_32 type) ;

#endif

crc.c文件

#include <stdio.h>
#include "type.h"
#include "CRC.h"

const CRC_8 crc_8 = {0x07,0x00,0x00,FALSE,FALSE};
const CRC_8 crc_8_ITU = {0x07,0x00,0x55,FALSE,FALSE};
const CRC_8 crc_8_ROHC = {0x07,0xff,0x00,TRUE,TRUE};
const CRC_8 crc_8_MAXIM = {0x31,0x00,0x00,TRUE,TRUE};

const CRC_16 crc_16_IBM = {0x8005,0x0000,0x0000,TRUE,TRUE};
const CRC_16 crc_16_MAXIM = {0x8005,0x0000,0xffff,TRUE,TRUE};
const CRC_16 crc_16_USB = {0x8005,0xffff,0xffff,TRUE,TRUE};
const CRC_16 crc_16_MODBUS = {0x8005,0xffff,0x0000,TRUE,TRUE};
const CRC_16 crc_16_CCITT = {0x1021,0x0000,0x0000,TRUE,TRUE};
const CRC_16 crc_16_CCITT_FALSE = {0x1021,0xffff,0x0000,FALSE,FALSE};
const CRC_16 crc_16_X5 = {0x1021,0xffff,0xffff,TRUE,TRUE};
const CRC_16 crc_16_XMODEM = {0x1021,0x0000,0x0000,FALSE,FALSE};
const CRC_16 crc_16_DNP = {0x3d65,0x0000,0xffff,TRUE,TRUE};

const CRC_32 crc_32 = {0x04c11db7,0xffffffff,0xffffffff,TRUE,TRUE};
const CRC_32 crc_32_MPEG2 = {0x04c11db7,0xffffffff,0x00000000,FALSE,FALSE};

/*****************************************************************************
*function name:reverse8
*function: 字节反转,如1100 0101 反转后为1010 0011
*input:1字节
*output:反转后字节
******************************************************************************/
u8 reverse8(u8 data)
{
    u8 i;
    u8 temp=0;
    for(i=0;i<8;i++)	//字节反转
        temp |= ((data>>i) & 0x01)<<(7-i);
    return temp;
}
/*****************************************************************************
*function name:reverse16
*function: 双字节反转,如1100 0101 1110 0101反转后为1010 0111 1010 0011
*input:双字节
*output:反转后双字节
******************************************************************************/
u16 reverse16(u16 data)
{
    u8 i;
    u16 temp=0;
    for(i=0;i<16;i++)		//反转
        temp |= ((data>>i) & 0x0001)<<(15-i);
    return temp;
}
/*****************************************************************************
*function name:reverse32
*function: 32bit字反转
*input:32bit字
*output:反转后32bit字
******************************************************************************/
u32 reverse32(u32 data)
{
    u8 i;
    u32 temp=0;
    for(i=0;i<32;i++)		//反转
        temp |= ((data>>i) & 0x01)<<(31-i);
    return temp;
}

/*****************************************************************************
*function name:crc8
*function: CRC校验,校验值为8位
*input:addr-数据首地址;num-数据长度(字节);type-CRC8的算法类型
*output:8位校验值
******************************************************************************/
u8 crc8(u8 *addr, int num,CRC_8 type)  
{  
    u8 data;
    u8 crc = type.InitValue;                   //初始值
    int i;  
    for (; num > 0; num--)               
    {  
        data = *addr++;
        if(type.InputReverse == TRUE)
        data = reverse8(data);                 //字节反转
        crc = crc ^ data ;                     //与crc初始值异或 
        for (i = 0; i < 8; i++)                //循环8位 
        {  
            if (crc & 0x80)                    //左移移出的位为1,左移后与多项式异或
                crc = (crc << 1) ^ type.poly;    
            else                               //否则直接左移
                crc <<= 1;                  
        }
    }
    if(type.OutputReverse == TRUE)             //满足条件,反转
        crc = reverse8(crc);
    crc = crc^type.xor;                        //最后返与结果异或值异或
    return(crc);                               //返回最终校验值
}

/*****************************************************************************
*function name:crc16
*function: CRC校验,校验值为16位
*input:addr-数据首地址;num-数据长度(字节);type-CRC16的算法类型
*output:16位校验值
******************************************************************************/
u16 crc16(u8 *addr, int num,CRC_16 type)  
{  
    u8 data;
    u16 crc = type.InitValue;					//初始值
    int i;  
    for (; num > 0; num--)               
    {  
        data = *addr++;
        if(type.InputReverse == TRUE)
            data = reverse8(data);				//字节反转
        crc = crc ^ (data<<8) ;					//与crc初始值高8位异或 
        for (i = 0; i < 8; i++)					//循环8位 
        {  
            if (crc & 0x8000)					//左移移出的位为1,左移后与多项式异或
                crc = (crc << 1) ^ type.poly;    
            else		                        //否则直接左移
                crc <<= 1;                  
        }
    }
    if(type.OutputReverse == TRUE)              //满足条件,反转
        crc = reverse16(crc);
    crc = crc^type.xor;	                        //最后返与结果异或值异或
    return(crc);                                //返回最终校验值
}
/*****************************************************************************
*function name:crc32
*function: CRC校验,校验值为32位
*input:addr-数据首地址;num-数据长度(字节);type-CRC32的算法类型
*output:32位校验值
******************************************************************************/
u32 crc32(u8 *addr, int num,CRC_32 type)  
{  
    u8 data;
    u32 crc = type.InitValue;					//初始值
    int i;  
    for (; num > 0; num--)               
    {  
        data = *addr++;
        if(type.InputReverse == TRUE)
            data = reverse8(data);				//字节反转
        crc = crc ^ (data<<24) ;				//与crc初始值高8位异或 
        for (i = 0; i < 8; i++)					//循环8位 
        {  
            if (crc & 0x80000000)				//左移移出的位为1,左移后与多项式异或
                crc = (crc << 1) ^ type.poly;    
            else                                //否则直接左移
                crc <<= 1;                  
        }
    }
    if(type.OutputReverse == TRUE)              //满足条件,反转
        crc = reverse32(crc);
    crc = crc^type.xor;	                        //最后返与结果异或值异或
    return(crc);                                //返回最终校验值
}

调用时,只需传入相应的参数即可。经验证全部正确。如有疑问请评论留言。

3.3.3 改进的CRC校验过程

3.3.2中的代码具有通用性,但是可以看到效率不高。以crc16函数为例,需要判断字节是否需要反转,结束时,也需要判断是否需要反转,这都会耗费时间,如果需要反转,那么反转函数要花费更多时间。如何能提高效率呢?实际中我们常用某一种具体的校验方法,所以可以写单独的代码而非通用的,这样就可以省去两次判断反转的时间。以crc16/MAXIM为例,开始和结束都需要反转,改进后可以省略,具体操作如下:

/*****************************************************************************
*function name:crc16_MAXIM
*function: CRC校验,校验值为16位
*input:addr-数据首地址;num-数据长度(字节)
*output:16位校验值
******************************************************************************/
u16 crc16_MAXIM(u8 *addr, int num)  
{  
    u8 data;
    u16 crc = 0x0000;//初始值
    int i;  
    for (; num > 0; num--)             
    {  
        crc = crc ^ (*addr++) ;     //低8位异或
        for (i = 0; i < 8; i++)             
        {  
            if (crc & 0x0001)       //由于前面和后面省去了反转,所以这里是右移,且异或的值为多项式的反转值
                crc = (crc >> 1) ^ 0xA001;//右移后与多项式反转后异或
            else                   //否则直接右移
                crc >>= 1;                    
        }                               
    }
    return(crc^0xffff);            //返回校验值 
}  

读者可对比通用代码中crc16函数和crc16_MAXIM函数的区别。

4.以CRC-8校验为例讲解查表法

查表法速度快,但是表要占一部分内存,这是以空间换时间。

为了便于理解查表法,首先看3.3.2中CRC.c中u8 crc8(u8 *addr, int num,CRC_8 type)函数。为了方便观察,我将此段函数单独放在下面:

/*****************************************************************************
*function name:crc8
*function: CRC校验,校验值为8位
*input:addr-数据首地址;num-数据长度(字节);type-CRC8的算法类型
*output:8位校验值
******************************************************************************/
u8 crc8(u8 *addr, int num,CRC_8 type)  
{  
    u8 data;
    u8 crc = type.InitValue;                   //初始值
    int i;  
    for (; num > 0; num--)               
    {  
        data = *addr++;
        if(type.InputReverse == TRUE)
        data = reverse8(data);                 //字节反转
        crc = crc ^ data ;                     //与crc初始值异或 
        for (i = 0; i < 8; i++)                //循环8位 
        {  
            if (crc & 0x80)                    //左移移出的位为1,左移后与多项式异或
                crc = (crc << 1) ^ type.poly;    
            else                               //否则直接左移
                crc <<= 1;                  
        }
    }
    if(type.OutputReverse == TRUE)             //满足条件,反转
        crc = reverse8(crc);
    crc = crc^type.xor;                        //最后返与结果异或值异或
    return(crc);                               //返回最终校验值
}

观察上面代码,仔细发现18-24行的for循环可以生成一个表。进入for循环的变量crc大小为1个字节,即变量crc的大小可以为0-255之间的任何一个值,并且也只能是0-255之间的一个值。因此,可以实现将0-255都经过这个for循环,生成对应的值,生成的值也是0-255,这些生成的值就是crc要查的表,而传入for循环的变量crc就是表的索引值

以下代码是生成8位crc表的代码(多项式为0x07,核心的代码为11-17行for循环):

void GenerateCrc8Table(u8 *crc8Table)  
{  
    u8 crc=0;
    u16 i,j;
    for(j = 0;j<256;j++)
    {
        if(!(j%16))                        //16个数为1行
            printf("\r\n");

        crc = (u8)j;
        for (i = 0; i < 8; i++)             
        {  
            if (crc & 0x80)               //最高位为1
                crc = (crc << 1) ^ 0x07;  //左移后与多项式异或
            else                          //否则直接左移
                crc <<= 1;                    
        }
        crc8Table[j] = crc;//取低字节
        printf("%2x ",crc);		
    }
    printf("\r\n");
}

生成的表如下:

需要注意的是,查表法所用的表根据多项式的不同而不同,所以再用不同多项式时,一定要重新生成对应的表。所以多项式为0x07的u8 crc8函数用查表法可以改写为如下:

/*****************************************************************************
*function name:crc8withTable
*function: CRC校验,校验值为8位
*input:addr-数据首地址;len-数据长度(字节);crc8Table-CRC8表
*output:8位校验值
******************************************************************************/
u8 crc8withTable(u8 *addr, int len,u8 *crc8Table)  
{  
    u8 data;
    u8 crc = 00;                   //初始值
    int i;  
    for (; len > 0; len--)               
    {  
        data = *addr++;
        crc = crc ^ data ;                     //与crc初始值异或 
        crc = crc8Table[crc];                  //替换下面for循环
//        for (i = 0; i < 8; i++)                //循环8位 
//        {  
//            if (crc & 0x80)                    //左移移出的位为1,左移后与多项式异或
//                crc = (crc << 1) ^ 0x07;    
//            else                               //否则直接左移
//                crc <<= 1;                  
//        }
    }
    crc = crc^0x00;                            //最后返与结果异或值异或
    return(crc);                               //返回最终校验值
}

可以看到第16行代码替换了for循环,直接从表中取值,速度快了很多。实际在用时,需要将表写成数组放在代码前面,这样代码就可以查表了。

5.以CRC-16校验为例讲解查表法

crc16查表法和crc8查表法类似,也是首先是生成一个表,然后再用查表代替for循环。

5.1.生成表格

以3.3.3中的CRC16代码为例,首先生成一个表,此表每个元素都是2字节,一共256个元素。但是需要将这个表拆分成两个表,其中高字节放在crcLowTable,低字节放在crcHighTable(我也不理解为什么这样做,但是按照这样的方法确实能实现查表法)。生成表代码如下:

/*****************************************************************************
*function name:GenerateCrc16Table
*function: 生成crc16查表法用的表格
*input: crcHighTable,crcLowTable:256大小的数组,即生成的表格首地址 
*output:无
******************************************************************************/
void GenerateCrc16Table(u8 *crcHighTable,u8 *crcLowTable)  
{  
    u16 crc=0;
    u16 i,j;
    for(j = 0;j<256;j++)
    {
        if(!(j%8))
            printf("\r\n");

        crc = j;
        for (i = 0; i < 8; i++)             
        {  
            if (crc & 0x0001)       //由于前面和后面省去了反转,所以这里是右移,且异或的值为多项式的反转值
                crc = (crc >> 1) ^ 0xA001;//右移后与多项式反转后异或
            else                   //否则直接右移
                crc >>= 1;                    
        }
        crcHighTable[j] = (u8)(crc&0xff);//取低字节
        crcLowTable[j] = (u8)((crc>>8)&0xff);//取高字节
        printf("%4x  ",crc);		
    }
    printf("\r\n");
}

生成表如下:

整体表

      

拆分后低字节 crcHighTable          拆分后高字节crcLowTable

5.2.查表法实现

有了表格后,就可以实现查表法了(为什么用15-17行代码我也不清楚,这里可能涉及到推倒,欢迎大神们在评论区指导),代码如下:

/*****************************************************************************
*function name:Crc16withTable
*function: 用查表法计算CRC
*input:  addr:字符串起始地址;len :字符串长度;table:用到的表格
*output:无
******************************************************************************/
u16 Crc16withTable(u8 *addr, int len,u8 *crcHighTable,u8 *crcLowTable)  
{  
    u8 crcHi = 0x00;
    u8 crcLo = 0x00;
    u8 index;
    u16 crc;
    for (;len>0;len--)             
    {  
        index = crcLo ^ *(addr++);//低8位异或,得到表格索引值
        crcLo = crcHi ^ crcHighTable[index];
        crcHi = crcLowTable[index];
    }
    crc = (u16)(crcHi<<8 | crcLo);
    return(crc^0xffff);//返回校验值
}

大家可以自行验证,将3.3.3代码和5.2代码计算结果作比较。结果是一样的。实际在用时,需要将表写成数组放在代码前面,这样代码就可以查表了。

6.代码链接

https://download.csdn.net/download/u013073067/13138335

编译环境:VS2010

语言:C

如有疑问,欢迎大家在评论区留言讨论。

参考文献:

[1]https://www.cnblogs.com/sinferwu/p/7904279.html

[2]https://en.wikipedia.org/wiki/Cyclic_redundancy_check

[3]https://www.cnblogs.com/94cool/p/3559585.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CRC校验详解(附代码示例) 的相关文章

  • 【AUTOSAR】【通信安全】CRC

    目录 一 概述 二 功能说明 2 1 通用行为 2 2 8位CRC计算 2 2 1 8位SAE J1850 CRC计算 2 2 2 8位0x2F多项式CRC计算 2 3 16位CRC计算 2 3 1 16位CCITT FALSE CRC16
  • 国密SM4对称加密算法(对本地文件的加解密)代码展示

    代码 package com example demo MIMAXUE SM import sun misc BASE64Decoder import sun misc BASE64Encoder import java io Buffer
  • 加密算法(一)

    加密算法 参考 https blog csdn net qq 31878855 article details 69396791 加密算法分类 常见的加密算法可以分成两类 对称加密算法和非对称加密算法 对称加密算法 加密和解密用的是同一串密
  • 【区块链】(四)之常见的加密算法

    我们经常在谍战片里看到 我军传递情报用电报发送 但敌人也可以截取电报 这就需要对电报发送的内容进行加密 当时常用的加密方式是通过一段密文 对情报进行加密 比如说是当天的日报 这种属于对称加密 差不多是DES加密算法 这里简单介绍几种 主要介
  • DES加密算法详解——看这一篇就够了!

    目录 一 DES简介 二 DES算法入参 三 DES加密算法步骤解析 1 IP置换 M gt M0 2 密钥K控制的16轮运算 M0 K1 K16 gt M16 2 1 子密钥Kn的计算 2 1 1 PC 1置换 2 1 2 循环左移运算
  • CRC校验(二)

    CRC校验 二 参考 https blog csdn net liyuanbhu article details 7882789 https www cnblogs com esestt archive 2007 08 09 848856
  • 需要帮助纠正用 Javascript (node.js) 编写的 CRC-ITU 检查方法中的问题

    我们正在尝试在 Javascript 上编写 GPS 设备侦听器代码 在此过程中 我们无法开发正确的 CRC ITU 错误检查脚本 协议文档生成crc码的解释如下 终端或服务器可以使用校验码来区分 接收到的信息是否有误 为了防止错误 数据传
  • 使用 32 位哈希时发生冲突的概率

    我的数据库中有一个 10 个字符的字符串键字段 我已经使用 CRC32 对该字段进行哈希处理 但我担心重复项 有人可以告诉我在这种情况下发生碰撞的可能性吗 P S 我的字符串字段在数据库中是唯一的 如果字符串字段的数量为 100 万个 那么
  • 初学者了解循环冗余码算法

    at PNG 规范第 5 5 节 它在称为 CRC 或 循环冗余码 的 PNG 文件格式中讨论了这个概念 我以前从未听说过它 所以我正在尝试了解它 采用的 CRC 多项式是 x32 x26 x23 x22 x16 x12 x11 x10 x
  • 匹配 STM32F0 和 zlib 中的 CRC32

    我正在研究运行 Linux 的计算机和 STM32F0 之间的通信链路 我想对我的数据包使用某种错误检测 并且由于 STM32F0 有 CRC32 硬件 并且我在 Linux 上有带有 CRC32 的 zlib 所以我认为在我的项目中使用
  • 在Python中计算modbus的CRC16

    首先 抱歉 我是初学者 我在 modbus 上得到以下字节序列 01 04 08 00 00 00 09 00 00 00 00f8 0c 该字节序列上粗体的 CRC 是正确的 但是 要检查 创建 CRC 我必须遵循设备规范 其中规定 错误
  • 带有 PL/pgSQL 的 CRC32 函数

    如何计算 32 位循环冗余校验 CRC 32 作为 PostgreSQL 中的函数 方法与MySQL http dev mysql com doc refman 5 7 en mathematical functions html func
  • CRC4 在 C 中的实现

    我修改了发现的实现here https stackoverflow com questions 28656471 how to confgure calculation of crc table 为 CRC4 构建表生成函数 如下所示 de
  • Python 中的 CRC16

    如何在Python中计算CRC16 在 Perl 中我会写这样的东西 use Digest CRC crc16 result crc16 str 我如何在Python中做同样的事情 这里有一个计算CRC16的库http pypi pytho
  • python套接字在逐行调试时工作正常,但在完整运行时无法工作[重复]

    这个问题在这里已经有答案了 我正在开发一个项目 该项目涉及传输文件 并为文件的每个块进行 CRC 校验和计算 例如此处为 40960 字节 我的问题是 当我逐行调试代码时 一切正常 但当我完全运行代码时 我在接收器端得到不同的 CRC 校验
  • 计算 HDLC 帧的 FCS(CRC)

    我有以下框架 7e 01 00 00 01 00 18 ef 00 00 00 b5 20 c1 05 10 02 71 2e 1a c2 05 10 01 71 00 6e 87 02 00 01 42 71 2e 1a 01 96 27
  • 文件的 CRC 检查

    我正在使用一个小型 FAT16 文件系统 并且想要为存储配置信息的单个 XML 文件生成 CRC 值 如果数据发生更改或损坏 我希望能够检查 CRC 以确定文件仍处于原始状态 问题是 如何将CRC值放入文件中 而不改变文件本身的CRC值 我
  • CRC-CCITT (0xFFFF) 功能?

    有人可以帮我用 Delphi 实现 CRC CCITT 0xFFFF 已经获得 Java 版本 但对如何将其移植到 Delphi 感到困惑 public static int CRC16CCITT byte bytes int crc 0x
  • 零填充缓冲区/文件的 CRC32 计算

    如果我想计算大量连续零字节的 CRC32 值 在给定零运行长度的情况下 是否可以使用恒定时间公式 例如 如果我知道我有 1000 个字节全部用零填充 有没有办法避免 1000 次迭代的循环 只是一个例子 对于这个问题 实际的零数量是无限的
  • 如何检测已更改的网页?

    在我的应用程序中 我使用 LWP 定期获取网页 无论如何 是否要检查两次连续提取之间网页是否在某些方面发生了变化 除了明确进行比较之外 是否有在较低协议层生成的任何签名 例如 CRC 可以提取并与旧签名进行比较以查看可能的更改 有两种可能的

随机推荐

  • 使用 Vue 3.0 和 Element UI 实现功能增加、按钮操作和查询框功能详解

    简介 Vue 3 0 和 Element UI 是当今流行的前端开发工具 结合它们可以轻松构建出强大的用户界面 本篇技术博客将详细介绍如何利用 Vue 3 0 和 Element UI 实现功能增加 按钮操作以及通过查询框输入信息来进行信息
  • JS 数组对象去重

    原数据 let arr goodsId 1 quota 12 skuId 1 goodsId 2 quota 12 skuId 2 goodsId 1 quota 12 skuId 1 去重后数据 let arr goodsId 1 quo
  • ubuntu系统整体克隆时遇到system back无法识别固态硬盘

    ubuntu系统整体克隆 使用System back软件 安装相关软件 1 安装System back 2安装 GParted 制作系统盘 1 启动system back软件 2制作镜像 系统恢复 1选择U盘启动 2系统分区 在配置环境的时
  • C语言字符串逆转

    define CRT SECURE NO WARNINGS include
  • shell编程一百例 day-1

    shell编程一百例 今晚份 能力有限 未按顺序 未完待续 1 输出 Hello World 2 猜数游戏 3 键盘输入三个数 升序输出
  • 麒麟系统调试

    一 raid 报错 mdadm RUN ARRAY failed Invalid argument 原因 zcat proc config gz 查看配置 驱动未加载全 解决办法 modprobe dm raid modprobe raid
  • 一道经典的C++题,关于分钱的问题,适合新手阅读(黑客X档案论坛题目) [c#]...

    前几天CSDN论坛的首页 看到一则帖子 题目是 一道经典的C 题 关于分钱的问题 适合新手阅读 黑客X档案论坛题目 链接如下 http blog csdn net gisfarmer archive 2009 02 08 3869236 a
  • 手术导航系统原理简介、主要工作及应用

    目录 1 手术导航系统简介 2 手术导航系统的工作原理及构造 3 手术导航系统通常需完成四项主要工作
  • 在Windows10环境安装CUDA11.7及PyTorch1.13--使用Nvidia RTX A4000开始炼丹之旅

    在Windows10环境安装CUDA11 7及PyTorch1 13 使用Nvidia RTX A4000开始炼丹之旅 前言 这个双十一 RTX3090矿卡反倒是涨价了 RTX3090Ti当然也涨价了 只好从x宝搞一只工包丽台RTX A40
  • QScintilla应用(1) 安装及简介

    相关网址 下载地址 在线文档 1 下载并解压 将下载的压缩包解压之后 得到以下目录结构 目录结构含义如下 目录名 用途 Qt4Qt5 编译文件夹 生成对应的DLL文件 designer Qt4Qt5 相关的设计师插件的编译文件夹 examp
  • C++ DLUT 上机作业(一)

    文章目录 C DLUT 上机作业 一 1 定义一个分数类如下 要求实现分数的初始化与设置 四则运算及约分以及输出等功能 并在主函数中测试 2 定义并测试日期类 Date 类包括私有数据成员 year month day 公有成员函数实现以下
  • SpringBoot中通过住注解方式使用Redis

    1 SpringBoot中Redis缓存注解的使用 首先在启动类上添加 enableCache注解 表示开始注解缓存功能 特别注意 Spring框架中所有的注解都是通过AOP的原理实现的 即Spring框架为我们创建代理对象 代理对象去实现
  • chatgpt赋能python:Python画布大小是如何设置的

    Python画布大小是如何设置的 介绍 在Python中 我们可以使用各种GUI库来绘制图形界面 无论您是使用Tkinter PyQt还是wxPython 设置画布大小都是一个必须了解的重要概念 画布大小决定了我们可以在屏幕上呈现多少信息
  • Docker技术入门

    文章目录 1 Dockerfile概念 2 Dockerfile 指令 FROM 指定基础镜像 RUN执行命令 CMD 容器启动命令 COPY 复制文件 ADD 更高级的复制文件 ENV 设置环境变量 ARG 构建参数 VOLUME 定义匿
  • 通达信板块监控指标_通达信板块监测指标公式

    通达信板块监测指标公式 稀缺资源 SUM 880505 C REF 880505 C 1 1 1000 0 COLORWHITE 军工航天 SUM 880507 C REF 880507 C 1 1 1000 0 COLORRED 智能电网
  • 小米路由器4A千兆版更换5G芯片和硬件布局后出现的刷机问题

    最近又入手一台小米路由器4A千兆版 打算通过 CH341A 编程器刷成老毛子的 结果一拆机傻眼了 整个电路板上的芯片和硬件布局都换了 这是老板子 这是新板子 两张图可以很清楚的看到有很大的变化 那么 之前的那种刷机方式还管用吗 经过测试后出
  • 程序员水平10分级,你的水平属于哪一级?

    随着技术发展 编程悄然融入了我们的生活 我们已然离不开那些程序和编程语言 很多人都在不同程度地谈论着如何编程 也诞生出很多编程语言排行 那么程序员到底应该如何分级呢 首先要明白什么是程序员 设计自己的Apache Web服务器的家伙 制作一
  • css3动画属性解析:【transform -变形】

    前两篇一起学习了animation和transition 的使用 今天我们终于开始学习transform和translate了 其实translate只是transform的一个属性 只是很多初学者对transform 变形 transla
  • Java 数据库中文变成问号???解决办法

    在连接的URL地址后面加上 url jdbc mysql localhost 3306 test useUnicode true characterEncoding utf8 于是在正式项目里面还发现一个 用于批处理的 还是加上吧 免得以后
  • CRC校验详解(附代码示例)

    目录 1 CRC校验原理 2 生成多项式 3 以CRC 16校验为例讲解编程实现 3 3 1 完全按照CRC原理实现校验 3 3 2 工程中常用CRC校验过程 3 3 3 改进的CRC校验过程 4 以CRC 8校验为例讲解查表法 5 以CR