首頁 > 軟體

C/C++實現crc碼計算和校驗

2023-03-11 06:02:17

演演算法介紹

迴圈冗餘校驗(Cyclic Redundancy Check, CRC)是一種根據網路封包或計算機檔案等資料產生簡短固定位數校驗碼的一種通道編碼技術,主要用來檢測或校驗資料傳輸或者儲存後可能出現的錯誤。它是利用除法及餘數的原理來作錯誤偵測的。

CRC校驗計算速度快,檢錯能力強,易於用編碼器等硬體電路實現。從檢錯的正確率與速度、成本等方面,都比奇偶校驗等校驗方式具有優勢。因而,CRC 成為計算機資訊通訊領域最為普遍的校驗方式。常見應用有乙太網/USB通訊,壓縮解壓,視訊編碼,影象儲存,磁碟讀寫等

引數模型

CRC引數模型

不知道你是否遇到過這種情況,同樣的CRC多項式,呼叫不同的CRC計算函數,得到的結果卻不一樣,而且和手算的結果也不一樣,這就涉及到CRC的引數模型了。計算一個正確的CRC值,需要知道CRC的引數模型。

一個完整的CRC引數模型應該包含以下資訊:WIDTH,POLY,INIT,REFIN,REFOUT,XOROUT。

NAME:引數模型名稱。

WIDTH:寬度,即生成的CRC資料位寬,如CRC-8,生成的CRC為8位元

POLY:十六進位制多項式,省略最高位1,如 x8 + x2 + x + 1,二進位制為1 0000 0111,省略最高位1,轉換為十六進位製為0x07。

INIT:CRC初始值,和WIDTH位寬一致。

REFIN:true或false,在進行計算之前,原始資料是否翻轉,如原始資料:0x34 = 0011 0100,如果REFIN為true,進行翻轉之後為0010 1100 = 0x2c

REFOUT:true或false,運算完成之後,得到的CRC值是否進行翻轉,如計算得到的CRC值:0x97 = 1001 0111,如果REFOUT為true,進行翻轉之後為11101001 = 0xE9。

XOROUT:計算結果與此引數進行互斥或運算後得到最終的CRC值,和WIDTH位寬一致。

接收端的校驗有兩種方式:

  • 一種是和CRC計算一樣,在本地把接收到的資料和CRC分離,然後在本地對資料進行CRC運算,得到的CRC值和接收到的CRC進行比較,如果一致,說明資料接收正確,如果不一致,說明資料有錯誤。
  • 另一種方法是把整個資料框進行CRC運算,因為是資料框相當於把原始資料左移8位元,然後加上餘數,如果直接對整個資料框進行CRC運算(除以多項式),那麼餘數應該為0,如果不為0說明資料出錯

程式碼計算

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

//求數的二進位制最高位的冪指數,即MSB
static int getMinPolynomialBits(uint64_t n) {
	int r = 0;
	while (n >>= 1) r++;
	return r;
}

//append>0表示計算crc校驗碼,賦值到crcRemainder
//append=0,表示校驗輸入bit流是否正確;0表示正確,-1表示錯誤
//此處的多項式預設為0x96(高位補1後的結果),預設crc位數為7,可根據程式碼自行修改
static int crcCheck(const char* msg, int append, char* crcRemainder)
{
	if (msg == NULL || crcRemainder == NULL || strlen(msg) == 0) {
		printf("input parameter is unvalid!n");
		return -1;
	}
	 //hex: 0x96 = b'10010110' = DEC:150 
	uint64_t poly = 0x96; 
	int polyLen = getMinPolynomialBits(poly + 1); //=7

	int msgLen = strlen(msg);
	//printf("%dn", msgLen);

	//計算crc校驗碼
	if (append) {
		unsigned char* pBufCrc = (unsigned char*)calloc(msgLen + polyLen, sizeof(unsigned char)); 
		memset(pBufCrc, 0, msgLen + polyLen);

		for (int j = 0; j < msgLen; j++) {
			pBufCrc[j] = msg[j] - '0';
		}

		uint8_t* p = NULL;
		for (int i = 0; i < msgLen; i++) {
			if (pBufCrc[i]) {
				p = pBufCrc + i + polyLen;
				uint64_t t = poly;
				do {
					*(p--) ^= t & 1;
				} while (t >>= 1);
			}
		}
		p = NULL;

		size_t k;
		for (k = 0; k < polyLen; k++) {
			crcRemainder[k] = pBufCrc[k + msgLen] + 48;
		}
		if (pBufCrc) {
			free(pBufCrc);
			pBufCrc = NULL;
		}
	}
	else {
		// 校驗接受端的位元流
		unsigned char* pBuffer = (unsigned char*)calloc(msgLen, sizeof(unsigned char)); 
		memset(pBuffer, 0, msgLen);

		int inforLen = msgLen - polyLen;//提取出資訊流部分,然後計算當前資訊對應crc校驗碼

		for (int j = 0; j < inforLen; j++) {
			pBuffer[j] = msg[j] - '0';
		}

		uint8_t* p = NULL;
		for (int i = 0; i < inforLen; i++) {
			if (pBuffer[i]) {
				p = pBuffer + i + polyLen;
				uint64_t t = poly;
				do {
					*(p--) ^= t & 1;
				} while (t >>= 1);
			}
		}
		p = NULL;

		//計算得到的crc碼和輸入的crc碼進行對比驗證,若每一位都相同,則校驗成功
		for (size_t k = inforLen; k < msgLen; k++) {
			if (msg[k] != pBuffer[k] + 48) {
				if (pBuffer) {
					free(pBuffer);
					pBuffer = NULL;
				}
				return -1;
			}
		}
		if (pBuffer) {
			free(pBuffer);
			pBuffer = NULL;
		}
	}
	return 0;
}

到此這篇關於C/C++實現crc碼計算和校驗的文章就介紹到這了,更多相關C++ crc碼內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com