C语言纯软件计算任意多项式CRC7、CRC8、CRC16和CRC32的代码

第一节 完全按照列竖式笔算模二除法的方式计算CRC7、CRC8、CRC16和CRC32

条件:初始值INIT=0,结果异或值XOROUT=0,输入数据不反转,输出数据不反转。

cpp 复制代码
/* CRC(循环冗余校验)在线计算: http://www.ip33.com/crc.html */
// 初始值INIT=0, 结果异或值XOROUT=0, 输入数据不反转, 输出数据不反转
#include <stdint.h>
#include <stdio.h>
#include <string.h>
 
#define POLYNOMIAL_CRC7 0x89ul
#define POLYNOMIAL_CRC8 0x107ul
#define POLYNOMIAL_CRC16 0x11021ul
#define POLYNOMIAL_CRC32 0x104c11db7ull
 
/* 计算CRC7校验码 */
uint8_t calc_crc7(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint16_t temp = 0;
 
	if (len != 0)
		temp |= p[0] << 8;
 
	for (i = 1; i <= len; i++)
	{
		if (i != len)
			temp |= p[i];
		for (j = 0; j < 8; j++)
		{
			if (temp & 0x8000)
				temp ^= POLYNOMIAL_CRC7 << 8;
			temp <<= 1;
		}
	}
	return temp >> 9;
}
 
/* 计算CRC8校验码 */
uint8_t calc_crc8(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint16_t temp = 0;
 
	if (len != 0)
		temp |= p[0] << 8;
 
	for (i = 1; i <= len; i++)
	{
		if (i != len)
			temp |= p[i];
		for (j = 0; j < 8; j++)
		{
			if (temp & 0x8000)
				temp ^= POLYNOMIAL_CRC8 << 7;
			temp <<= 1;
		}
	}
	return temp >> 8;
}
 
/* 计算CRC16校验码 */
uint16_t calc_crc16(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint32_t temp = 0;
 
	if (len != 0)
		temp |= p[0] << 24;
	if (len > 1)
		temp |= p[1] << 16; // 填充前二分之一
	if (len > 2)
		temp |= p[2] << 8; // 填充到四分之三
 
	for (i = 3; i <= len + 2; i++)
	{
		if (i < len)
			temp |= p[i]; // 每次都填充最后四分之一的空间
		
		// 从左数第0~7位计算到左数第16~23位
		// 当j=0时是从左数第0位计算到第16位, 共计17位
		// 当j=1时是从左数第1位计算到第17位, 共计17位
		// ...
		// 当j=7时是从左数第7位计算到第23位, 共计17位
		for (j = 0; j < 8; j++)
		{
			if (temp & 0x80000000)
				temp ^= POLYNOMIAL_CRC16 << 15;
			temp <<= 1;
		}
	}
	return temp >> 16;
}
 
/* 计算CRC32校验码 */
uint32_t calc_crc32(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint64_t temp = 0;
 
	if (len != 0)
		temp |= (uint64_t)p[0] << 56;
	if (len > 1)
		temp |= (uint64_t)p[1] << 48;
	if (len > 2)
		temp |= (uint64_t)p[2] << 40;
	if (len > 3)
		temp |= (uint64_t)p[3] << 32; // 填充前二分之一
	if (len > 4)
		temp |= (uint64_t)p[4] << 24;
	if (len > 5)
		temp |= (uint64_t)p[5] << 16;
	if (len > 6)
		temp |= (uint64_t)p[6] << 8; // 填充到八分之七
 
	for (i = 7; i <= len + 6; i++)
	{
		if (i < len)
			temp |= p[i]; // 每次都填充最后八分之一的空间
		
		// 从左数第0~7位计算到左数第32~39位
		// 当j=0时是从左数第0位计算到第32位, 共计33位
		// 当j=1时是从左数第1位计算到第33位, 共计33位
		// ...
		// 当j=7时是从左数第7位计算到第39位, 共计33位
		for (j = 0; j < 8; j++)
		{
			if (temp & 0x8000000000000000)
				temp ^= POLYNOMIAL_CRC32 << 31;
			temp <<= 1;
		}
	}
	return temp >> 32;
}
 
void main()
{
	char *str = "https://zh.purasbar.com/";
	int i, n;
 
	n = strlen(str);
	for (i = 0; i <= n; i++)
		printf("len=%d, crc7=0x%02x, crc8=0x%02x, crc16=0x%04x, crc32=0x%08x\n", i, calc_crc7(str, i), calc_crc8(str, i), calc_crc16(str, i), calc_crc32(str, i));
}

程序运行结果:


第二节 计算反转式CRC32

条件:初始值0xffffffff、结果异或值0xffffffff、输入数据反转、输出数据反转。

cpp 复制代码
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#define INIT_CRC32 0xffffffffull
#define POLYNOMIAL_CRC32 0x104c11db7ull

uint8_t reverse(uint8_t data)
{
	int i;
	uint8_t result = 0;

	for (i = 0; i < 8; i++)
	{
		result <<= 1;
		if (data & 1)
			result |= 1;
		data >>= 1;
	}
	return result;
}

uint32_t reverse32(uint32_t data)
{
	uint32_t result;

	result = reverse((data >> 24) & 0xff);
	result |= reverse((data >> 16) & 0xff) << 8;
	result |= reverse((data >> 8) & 0xff) << 16;
	result |= reverse(data & 0xff) << 24;
	return result;
}

/* 计算CRC32校验码(初始值0xffffffff、结果异或值0xffffffff、输入数据反转、输出数据反转) */
uint32_t calc_crc32_rev(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint64_t temp = INIT_CRC32 << 32;
 
	if (len != 0)
		temp ^= (uint64_t)reverse(p[0]) << 56;
	if (len > 1)
		temp ^= (uint64_t)reverse(p[1]) << 48;
	if (len > 2)
		temp ^= (uint64_t)reverse(p[2]) << 40;
	if (len > 3)
		temp ^= (uint64_t)reverse(p[3]) << 32; // 填充前二分之一
	if (len > 4)
		temp |= (uint64_t)reverse(p[4]) << 24;
	if (len > 5)
		temp |= (uint64_t)reverse(p[5]) << 16;
	if (len > 6)
		temp |= (uint64_t)reverse(p[6]) << 8; // 填充到八分之七
 
	for (i = 7; i <= len + 6; i++)
	{
		if (i < len)
			temp |= reverse(p[i]); // 每次都填充最后八分之一的空间
		
		// 从左数第0~7位计算到左数第32~39位
		// 当j=0时是从左数第0位计算到第32位, 共计33位
		// 当j=1时是从左数第1位计算到第33位, 共计33位
		// ...
		// 当j=7时是从左数第7位计算到第39位, 共计33位
		for (j = 0; j < 8; j++)
		{
			if (temp & 0x8000000000000000)
				temp ^= POLYNOMIAL_CRC32 << 31;
			temp <<= 1;
		}
	}
	return reverse32(temp >> 32) ^ 0xffffffff;
}

void main()
{
	char *str = "https://zh.purasbar.com/";
	int i, n;

	n = strlen(str);
	for (i = 0; i <= n; i++)
		printf("len=%d, crc32_rev=0x%08x\n", i, calc_crc32_rev(str, i));
}

程序运行结果:


第三节 不使用uint64_t变量计算CRC32

有的单片机不支持uint64_t类型,无法运行calc_crc32函数和calc_crc32_rev函数。

注意到

if (temp & 0x8000000000000000)

temp ^= POLYNOMIAL_CRC32 << 31;

temp <<= 1;

也就是

if (temp & 0x8000000000000000)

{

temp ^= POLYNOMIAL_CRC32 << 31;

temp <<= 1;

}

else

temp <<= 1;

相当于

if (temp & 0x8000000000000000)

{

temp <<= 1;

temp ^= POLYNOMIAL_CRC32 << 32;

}

else

temp <<= 1;

这样的话,我们就可以把一个uint64_t temp变量拆成两个uint32_t变量,使calc_crc32函数和calc_crc32_rev函数可以在不支持uint64_t类型的平台上运行。

因为异或运算由33位变成了32位,所以模二除法的除数POLYNOMIAL_CRC32=0x104c11db7最高位的1就不再需要了,可以删掉,变成0x4c11db7。

cpp 复制代码
#include <stdint.h>
#include <stdio.h>
#include <string.h>
 
#define POLYNOMIAL_CRC32 0x4c11db7
 
/* 计算CRC32校验码 */
uint32_t calc_crc32(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint32_t temp = 0;
	uint32_t temp2 = 0;
	uint32_t msb;
 
	// 填充前二分之一
	if (len != 0)
		temp |= p[0] << 24;
	if (len > 1)
		temp |= p[1] << 16;
	if (len > 2)
		temp |= p[2] << 8;
	if (len > 3)
		temp |= p[3];
 
	// 填充到八分之七
	if (len > 4)
		temp2 |= p[4] << 24;
	if (len > 5)
		temp2 |= p[5] << 16;
	if (len > 6)
		temp2 |= p[6] << 8;
 
	for (i = 7; i <= len + 6; i++)
	{
		if (i < len)
			temp2 |= p[i]; // 每次都填充最后八分之一的空间
		
		// 从左数第0~7位计算到左数第32~39位
		// 当j=0时是从左数第0位计算到第32位, 共计33位
		// 当j=1时是从左数第1位计算到第33位, 共计33位
		// ...
		// 当j=7时是从左数第7位计算到第39位, 共计33位
		for (j = 0; j < 8; j++)
		{
			msb = (temp & 0x80000000);
			
			temp <<= 1;
			if (temp2 & 0x80000000)
				temp |= 1;
			temp2 <<= 1;
 
			if (msb)
				temp ^= POLYNOMIAL_CRC32;
		}
	}
	return temp;
}
 
uint8_t reverse(uint8_t data)
{
	int i;
	uint8_t result = 0;
 
	for (i = 0; i < 8; i++)
	{
		result <<= 1;
		if (data & 1)
			result |= 1;
		data >>= 1;
	}
	return result;
}
 
uint32_t reverse32(uint32_t data)
{
	uint32_t result;
 
	result = reverse((data >> 24) & 0xff);
	result |= reverse((data >> 16) & 0xff) << 8;
	result |= reverse((data >> 8) & 0xff) << 16;
	result |= reverse(data & 0xff) << 24;
	return result;
}
 
/* 计算CRC32校验码(初始值0xffffffff、结果异或值0xffffffff、输入数据反转、输出数据反转) */
uint32_t calc_crc32_rev(const void *data, int len)
{
	const uint8_t *p = data;
	int i, j;
	uint32_t temp = 0xffffffff;
	uint32_t temp2 = 0;
	uint32_t msb;
 
	// 填充前二分之一
	if (len != 0)
		temp ^= reverse(p[0]) << 24;
	if (len > 1)
		temp ^= reverse(p[1]) << 16;
	if (len > 2)
		temp ^= reverse(p[2]) << 8;
	if (len > 3)
		temp ^= reverse(p[3]);
 
	// 填充到八分之七
	if (len > 4)
		temp2 |= reverse(p[4]) << 24;
	if (len > 5)
		temp2 |= reverse(p[5]) << 16;
	if (len > 6)
		temp2 |= reverse(p[6]) << 8;
 
	for (i = 7; i <= len + 6; i++)
	{
		if (i < len)
			temp2 |= reverse(p[i]); // 每次都填充最后八分之一的空间
		
		// 从左数第0~7位计算到左数第32~39位
		// 当j=0时是从左数第0位计算到第32位, 共计33位
		// 当j=1时是从左数第1位计算到第33位, 共计33位
		// ...
		// 当j=7时是从左数第7位计算到第39位, 共计33位
		for (j = 0; j < 8; j++)
		{
			msb = (temp & 0x80000000);
			
			temp <<= 1;
			if (temp2 & 0x80000000)
				temp |= 1;
			temp2 <<= 1;
 
			if (msb)
				temp ^= POLYNOMIAL_CRC32;
		}
	}
	return reverse32(temp) ^ 0xffffffff;
}
 
void main()
{
	char *str = "https://zh.purasbar.com/";
	int i, n;
 
	n = strlen(str);
	for (i = 0; i <= n; i++)
		printf("len=%d, crc32=0x%08x, crc32_rev=0x%08x\n", i, calc_crc32(str, i), calc_crc32_rev(str, i));
}

程序运行结果:

相关推荐
承渊政道3 小时前
Linux系统学习【Linux系统的进度条实现、版本控制器git和调试器gdb介绍】
linux·开发语言·笔记·git·学习·gitee
JQLvopkk4 小时前
C# 轻量级工业温湿度监控系统(含数据库与源码)
开发语言·数据库·c#
玄同7654 小时前
从 0 到 1:用 Python 开发 MCP 工具,让 AI 智能体拥有 “超能力”
开发语言·人工智能·python·agent·ai编程·mcp·trae
czy87874754 小时前
深入了解 C++ 中的 `std::bind` 函数
开发语言·c++
消失的旧时光-19434 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
yq1982043011564 小时前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端
一个public的class4 小时前
你在浏览器输入一个网址,到底发生了什么?
java·开发语言·javascript
Jinkxs4 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&4 小时前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
BD_Marathon5 小时前
设计模式——依赖倒转原则
java·开发语言·设计模式