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校验详解(附代码示例) 的相关文章

  • 隐私计算分类

    在大数据时代中 海量的数据的交叉计算和人工智能的发展为各行各业提供了更好的支持 但这些被使用的数据往往包含用户的隐私数据 或企业 机构的内部数据 这些数据由于数据安全和隐私的考虑 往往是不对外开发 例如政府数据由于政策保密性完全不能对外公布
  • 加密算法(一)

    加密算法 参考 https blog csdn net qq 31878855 article details 69396791 加密算法分类 常见的加密算法可以分成两类 对称加密算法和非对称加密算法 对称加密算法 加密和解密用的是同一串密
  • CRC原理介绍及STM32 CRC外设的使用

    1 CRC简介 循环冗余校验 英语 Cyclic redundancy check 简称CRC 由 W Wesley Peterson 于 1961 年首次提出的一种纠错码理论 CRC是一种数据纠错方法 主要应用于数据通信或者数据存储的场合
  • 动手学区块链学习笔记(一):加密算法介绍

    引言 本文根据实验楼以及自己查询到的一些资料 文末给出 模拟了一下区块链从诞生到交易的整个过程 也算是弥补了一下之前区块链的一些缺失知识 哈希加密原理介绍 什么是比特币 比特币是一种加密货币 也是一种分布式数字货币 它的创建者使用匿名身份被
  • 【区块链】(四)之常见的加密算法

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

    CRC Cyclic Redundancy Check 被广泛用于数据通信过程中的差错检测 具有很强的 检错能力 本文详细介绍了CRC的基本原理 并且按照解释通行的查表算法的由来的思路介绍 了各种具体的实现方法 1 差错检测 数据通信中 接
  • python3 hashlib库sha256、pbkdf2_hmac、blake2b基本用法

    hashlib sha256 import hashlib x hashlib sha256 x update b asd print x 1 x hexdigest x hashlib sha256 x update asd encode
  • 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
  • CRC校验关键点说明(内附C语言CRC校验库)

    文章目录 目的 CRC校验关键点 参数模型 计算方式 CRC校验库 源文件 使用测试 总结 目的 CRC即循环冗余校验码 Cyclic Redundancy Check 是数据通信领域中最常用的一种查错校验码 其特征是信息字段和校验字段的长
  • CRC 和哈希方法(MD5、SHA1)的区别

    CRC和散列方法都可以用来验证原始数据的完整性 为什么现在大多数系统都使用哈希方法 CRC 旨在防止传输错误 而不是恶意操作 因此 它不耐碰撞 特别是 CRC 码的线性特性甚至允许攻击者以保持校验值不变的方式修改消息
  • XAPK 文件验证显示正确的数据信息

    在运行 APK 扩展文件示例时 我只是跳过了检查 CRC32 以避免 crc bug 算法 并且它工作得非常好 但是 虽然 XAPK 文件验证显示正确的下载数据信息为 99 如何避免这种情况并通过硬编码查看完整的 100 这不是最好的解决方
  • 需要帮助纠正用 Javascript (node.js) 编写的 CRC-ITU 检查方法中的问题

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

    我需要使用 Python 计算此 CRC 以便与 Aurora ABB 太阳能逆变器进行通信 这是文件 http www drhack it images PDF AuroraCommunicationProtocol 4 2 pdf在最后
  • _mm_crc32_u8 给出的结果与参考代码不同

    我一直在与内在因素作斗争 特别是使用标准 CRC 计算和据推测等效的英特尔内在函数 我想转而使用 mm crc32 u16 and mm crc32 u32但如果我不能让 8 位操作工作 那就没有意义了 static UINT32 g ui
  • 是否可以使用 CRC 进行基本的纠错?

    我知道使用 CRC 的全部目的是进行错误检测 但我听到有人说它除了错误检测之外还可以用于进行基本的错误纠正 我很好奇是否是这样 如果是的话 它的威力有多大 我的意思是 我们通常将 CRC 称为能够执行 x 位检测 但我很好奇它是否能够执行
  • 在Python中计算modbus的CRC16

    首先 抱歉 我是初学者 我在 modbus 上得到以下字节序列 01 04 08 00 00 00 09 00 00 00 00f8 0c 该字节序列上粗体的 CRC 是正确的 但是 要检查 创建 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 算法

    我正在尝试找到适用于以下结果的 crc 字节串由 2 个字节组成 即 0xCE1E crc 是单个字节 即 0x03 byte crc CE1E 03 CE20 45 CE22 6F 0000 C0 0001 D4 FFFF 95 有人可以
  • ruby: `read': File.read 处的参数无效 -(Errno::EINVAL)

    我正在做一个简单的脚本来检查所有文件的 crc require zlib exit if Object const defined Ocra files Dir glob File open dir txt a do file file p

随机推荐

  • 使用 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