crc32校验的c语言实现

本文介绍了CRC校验的基本原理及其实现方法,包括CRC校验的生成多项式、查表法实现步骤,并通过示例代码展示了如何生成CRC表及进行CRC校验。

最近在做软件升级,需要对升级文件进行crc校验,就学习了crc的实现原理


crc就是一个数值,该数值用于检验数据的正确性,crc校验的原理就是将需要作校验的数据与一个数据模2相除,得到的余数即为校验值。

      模2相除就是在除的过程中用模2加,模2加实际上就是异或运算,就是不进行进位操作,即相同为假,不相同为真。

下面是几种CRC校验的生成多项式:

CRC8 = X8+X5+X4+1

CRC-CCITT = X16+X12+X5+1

CRC16=X16+X15+X2+1

CRC12=X12+X11+X3+X2+1

CRC32=X32+X26+X23+X22+X16+X12+X11+X10+X8+X7+X5+X4+X2+X+1

一个多项式就是一个比特流,也就是由0、1组合起来的一组数

我们需要获取的常数就是上面多项式所对应的反转多项式(反转多项式:在数据通讯时,信息字节先传送或接受低位字节,如重新排列则影响速度)。

 

下面使用查表法实现crc校验,具体步骤如下:

(1)将上次计算出的CRC校验码右移一个字节;
(2)将移出的这个字节与新的要校验的字节进行XOR 运算;
(3)用运算出的值在预先生成码表中进行索引,获取对应的值(称为余式);
(4)用获取的值与第(1)步右移后的值进行XOR 运算;
(5)如果要校验的数据已经处理完,则第(4)步的结果就是最终的CRC校验码。如果还有数据要进行处理,则再转到第(1)步运行。


本例使用crc32校验,使用上面crc32的生成多项式,值为0xEDB88320


首先写一个功能函数,实现生成一个crc表,然后写一个功能函数,用于计算crc值,最后再写一个函数用于比较crc值,下面是全部代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdint.h>

uint32_t crc32_table[256];

int make_crc32_table()
{
	uint32_t c;
	int i = 0;
	int bit = 0;
	
	for(i = 0; i < 256; i++)
	{
		c  = (uint32_t)i;
		
		for(bit = 0; bit < 8; bit++)
		{
			if(c&1)
			{
				c = (c >> 1)^(0xEDB88320);
			}
			else
			{
				c =  c >> 1;
			}
			
		}
		crc32_table[i] = c;
	}
	

}

uint32_t make_crc(uint32_t crc, unsigned char *string, uint32_t size)
{

	while(size--)
		crc = (crc >> 8)^(crc32_table[(crc ^ *string++)&0xff]);

	return crc;
}

void compare_crc(char *filename)
{
	FILE *sp = NULL;
	uint32_t srcCrc ;
	uint32_t calcuCrc = 0xffffffff;
	unsigned char buf[1024];
	uint32_t count;
	
	if(filename == NULL)
	{
		printf("filename is null\n");
		exit(1);
	}

	sp = fopen(filename, "rb");
	if(sp == NULL)
	{
		printf("open file fail\n");
		exit(1);
	}

	fread(&srcCrc, 1, 4, sp);
	printf("In %s: src crc is 0x%x\n", __FUNCTION__, srcCrc);	


        if(sp)
        {
                while(!feof(sp))
                {
                        memset(buf, 0, sizeof(buf));
                        count = fread(buf, 1, sizeof(buf), sp);
                        calcuCrc = make_crc(calcuCrc, buf, count);
                }
        }
	printf("In %s: calcuCrc is 0x%x\n", __FUNCTION__, calcuCrc);

	fclose(sp);

	if(srcCrc == calcuCrc)
	{
		printf("In %s: the calculate crc equal the src crc in file \n", __FUNCTION__);

	}
	else
	{
		printf("In %s: the calculate crc not equal the src crc in file \n", __FUNCTION__);
	}
	
}


int main()
{
	int i;
	FILE *sp = NULL;
	FILE *dp = NULL;
	uint32_t count;
	uint32_t crc = 0xFFFFFFFF;
	unsigned char buf[1024];
	

	make_crc32_table();

	sp = fopen("/home/user/work_soft/crc_check/bak/test.txt", "rb");
	if(sp == NULL)
	{
		printf("open file error\n");
		return -1;
	}
	dp = fopen("/home/user/work_soft/crc_check/bak/testcrc.txt", "wb");
	if(dp == NULL)
	{
		printf("open file error\n");
		return -1;
	}

	if(sp)
	{
		while(!feof(sp))
		{
			memset(buf, 0, sizeof(buf));
			count = fread(buf, 1, sizeof(buf), sp);
			crc = make_crc(crc, buf, count);		
		}
	}

	printf("In main: calculate crc is 0x%x\n", crc);
	if(dp)
	{
		fwrite(&crc, 1, 4, dp);//write the crc into the file testcrc.txt
		fseek(sp, 0, SEEK_SET);
		while(!feof(sp))
		{
			memset(buf, 0, sizeof(buf));
                        count = fread(buf, 1, sizeof(buf), sp);
			fwrite(buf, 1, count, dp);
		}
		
		fclose(sp);
		fclose(dp);
	}

	/*compare crc*/
	compare_crc("/home/user/work_soft/crc_check/bak/testcrc.txt");
	

	return 0;

}

上面代码是根据查表法实现的,首先生成crc表,make_crc32_table就是根据crc校验的原理实现的,对0-255进行模2除法生成crc表;

函数make_crc是根据查表法的步骤实现的;而函数compare_crc则用来打开一个在头部嵌入crc值的文件,首先将文件头部的crc值取出(若用于功能实现,可设计一个数据结构用于存放crc等相关值,并存放在文件的头部),然后再读取文件的余下内容进行

crc计算,将计算出的crc值与从文件中读出的crc值进行比较,若相等则说明文件内容没有出错。下面是运行结果:




如果要将crc嵌入到文件头部,比如将计算得出的crc嵌入到升级文件的头部,由于我们生成的crc值的存储方式可能会因为不同的主机而不同(大端或小端)

所以在将crc值嵌入到升级文件的头部时,最好再加一个字节用于说明crc值得存储方式是大端还是小端模式,这样接收方就可以选择同一个存储方式,来确定读取的crc值是正确的

<think>首先,用户的问题是:"我想了解CRC32校验相关信息 请问CRC32校验的原理、应用及实现方法"。用户提到了CRC32校验,所以我需要基于提供的引用信息来回答。 引用信息总结: - 引用[1]:讨论了CRC校验原理,包括基本概念、计算、发送端和接收端校验,以及电路设计实现。 - 引用[2]:定义了CRC16,指出CRC是一种校验方法,应用于通信和存储领域。 - 引用[3]:讨论了CRC32的标准选择,如CRC-32/ISO-HDLC等。 - 引用[4]:详细解释了CRC算法原理,包括多项式表示和计算过程。 - 引用[5]:提到了移位寄存器实现的代码示例。 用户指定了CRC32,所以我应该专注于CRC32校验。 系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如:[^1] 回答结构: - 结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户的问题分解: 1. 原理:CRC32校验的原理。 2. 应用:CRC32校验的应用。 3. 实现方法:CRC32校验实现方法。 基于引用信息: - 原理:从引用[4]中,CRC算法原理是将数据块表示为多项式,除以一个生成多项式,得到余数作为校验码。 - 应用:从引用[2]和[3],CRC广泛应用于通信协议、存储系统等。 - 实现方法:从引用[1]和[5],有串行和并行实现,包括Verilog代码。 关键点: - CRC32使用32校验码。 - 生成多项式如CRC-32/ISO-HDLC是$x^{32} + x^{26} + x^{23} + x^{22} + x^{16} + x^{12} + x^{11} + x^{10} + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1$(标准多项式)。 - 计算过程:数据多项式乘以$x^r$,除以生成多项式,余数为校验码。 回答结构: 1. **原理部分**:解释CRC32的基本原理,包括多项式除法。 2. **应用部分**:列出常见应用场景。 3. **实现方法部分**:讨论软件和硬件实现方法。 4. **生成相关问题**:在最后添加相关问题。 添加引用标识: - 自然地添加在相关内容末尾。 使用数学表达式: - 行内:如$M' = M x^r$ - 独立:如生成多项式公式。 完整回答: - 介绍CRC32。 - 详细说明原理。 - 讨论应用。 - 描述实现方法。 - 添加相关问题。 确保中文响应。 </think>### CRC32校验原理、应用及实现方法 CRC32(Cyclic Redundancy Check,循环冗余校验)是一种基于多项式除法的数据校验方法,用于检测数据传输或存储中的错误。它生成一个32位的校验码(checksum),广泛应用于通信协议、存储系统等领域。下面我将从原理、应用和实现方法三个方面详细解释。内容基于提供的引用信息整合而成。 #### 1. CRC32校验原理 CRC32的核心原理是将数据块视为一个多项式,并通过除以一个预定义的生成多项式来生成校验码。生成多项式是CRC32算法的关键,常见标准如CRC-32/ISO-HDLC(表示为 $G(x) = x^{32} + x^{26} + x^{23} + x^{22} + x^{16} + x^{12} + x^{11} + x^{10} + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1$)[^3]。计算过程分为发送端和接收端: - **发送端计算**: 将原始数据块(长度为 k 位)表示为多项式 $M(x)$。例如,数据块"11100110"(MSB优先)对应 $M(x) = x^7 + x^6 + x^5 + x^2 + x$。然后,在数据后添加 r 个零位(r为生成多项式的次数,CRC32中 r=32),形成新多项式 $M'(x) = M(x) \times x^r$。使用生成多项式 $G(x)$ 对 $M'(x)$ 进行模2除法(二进制除法),得到的余数多项式 $R(x)$(次数小于 r)对应的32位值就是校验码[^4]。 - **接收端校验**: 接收方将接收到的数据和校验码一起除以相同的 $G(x)$。如果余数为零,表明数据无误(校验通过);否则,数据或校验码中有错误(如数据段出错或校验码段出错)[^1]。 CRC32的优势是高错误检测率(能检测单比特错误、突发错误等),且计算高效。多项式操作的实质是二进制位的异或运算,确保了硬件友好性[^2][^5]。 #### 2. CRC32校验应用 CRC32因其高效性和可靠性,在多个领域被广泛采用: - **通信协议**:用于以太网、USB、蓝牙等协议中检测数据传输错误,确保数据完整性[^2]。 - **存储系统**:在文件系统(如ZIP、GZIP压缩格式)中校验存储数据,防止文件损坏;例如,ZIP文件使用CRC32验证文件完整性[^3]。 - **嵌入式系统**:在FPGA或微控制器中实现实时校验,适用于工业控制或物联网设备[^1][^5]。 - **网络安全**:作为轻量级校验方法,用于数据包验证[^4]。 这些应用基于CRC32的低延迟和高检测能力,特别适合资源受限的系统[^2]。 #### 3. CRC32校验实现方法 CRC32实现分为软件和硬件两种方式,核心是基于移位寄存器和异或运算。实现时需注意选择标准生成多项式(如CRC-32/IEEE 802.3)。 - **软件实现(C语言示例)**: 使用查表法(LUT)优化计算速度。原理是将数据字节输入一个256-entry的查找表,预计算出中间余数。以下是一个简化代码示例: ```c uint32_t crc32_table[256]; // 预计算表 void init_crc32_table() { uint32_t polynomial = 0xEDB88320; // CRC-32/IEEE标准多项式 for (uint32_t i = 0; i < 256; i++) { uint32_t crc = i; for (int j = 0; j < 8; j++) { crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); } crc32_table[i] = crc; } } uint32_t compute_crc32(uint8_t *data, size_t len) { uint32_t crc = 0xFFFFFFFF; // 初始值 for (size_t i = 0; i < len; i++) { crc = (crc >> 8) ^ crc32_table[(crc ^ data[i]) & 0xFF]; } return ~crc; // 取反作为最终校验码 } ``` 此方法高效,适合PC或嵌入式软件实现[^4][^5]。 - **硬件实现(FPGA/Verilog)**: 分为串行和并行方式: - **串行实现**:使用线性移位寄存器(LFSR),每个时钟周期处理一个比特。例如,基于引用[5]的移位寄存器代码: ```verilog module crc32_serial(input clk, input reset, input data_in, output reg [31:0] crc_out); reg [31:0] shift_reg; always @(posedge clk or posedge reset) begin if (reset) shift_reg <= 32'hFFFFFFFF; else begin shift_reg[0] <= shift_reg[31] ^ data_in; // 基于多项式异或 shift_reg[1] <= shift_reg[0] ^ (shift_reg[31] ? 1'b1 : 1'b0); // 简化逻辑 // 类似实现其他位,具体取决于多项式 shift_reg[31:2] <= shift_reg[30:1]; // 移位操作 end end assign crc_out = ~shift_reg; // 输出校验码 endmodule ``` 此方法资源占用少,但速度慢[^1][^5]。 - **并行实现**:单个时钟周期处理多个比特(如8位),通过组合逻辑展开。优势是高吞吐量,适合高速系统,但电路更复杂[^1]。仿真结果显示,并行实现能显著减少延迟[^1]。 实现时需注意初始化值(通常为0xFFFFFFFF)和最终取反操作,以适配标准[^4]。 ### 相关问题 1. CRC32CRC16的区别是什么,各自适用场景如何? 2. 如何优化CRC32在嵌入式系统中的性能? 3. CRC32在常见文件格式(如ZIP)中的具体应用细节? 4. CRC32的错误检测能力有哪些局限性?
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值