C/C++實(shí)現(xiàn)crc碼計(jì)算和校驗(yàn)
算法介紹
循環(huán)冗余校驗(yàn)(Cyclic Redundancy Check, CRC)是一種根據(jù)網(wǎng)絡(luò)數(shù)據(jù)包或計(jì)算機(jī)文件等數(shù)據(jù)產(chǎn)生簡短固定位數(shù)校驗(yàn)碼的一種信道編碼技術(shù),主要用來檢測或校驗(yàn)數(shù)據(jù)傳輸或者保存后可能出現(xiàn)的錯(cuò)誤。它是利用除法及余數(shù)的原理來作錯(cuò)誤偵測的。
CRC校驗(yàn)計(jì)算速度快,檢錯(cuò)能力強(qiáng),易于用編碼器等硬件電路實(shí)現(xiàn)。從檢錯(cuò)的正確率與速度、成本等方面,都比奇偶校驗(yàn)等校驗(yàn)方式具有優(yōu)勢。因而,CRC 成為計(jì)算機(jī)信息通信領(lǐng)域最為普遍的校驗(yàn)方式。常見應(yīng)用有以太網(wǎng)/USB通信,壓縮解壓,視頻編碼,圖像存儲(chǔ),磁盤讀寫等
參數(shù)模型
CRC參數(shù)模型
不知道你是否遇到過這種情況,同樣的CRC多項(xiàng)式,調(diào)用不同的CRC計(jì)算函數(shù),得到的結(jié)果卻不一樣,而且和手算的結(jié)果也不一樣,這就涉及到CRC的參數(shù)模型了。計(jì)算一個(gè)正確的CRC值,需要知道CRC的參數(shù)模型。
一個(gè)完整的CRC參數(shù)模型應(yīng)該包含以下信息:WIDTH,POLY,INIT,REFIN,REFOUT,XOROUT。
NAME:參數(shù)模型名稱。
WIDTH:寬度,即生成的CRC數(shù)據(jù)位寬,如CRC-8,生成的CRC為8位
POLY:十六進(jìn)制多項(xiàng)式,省略最高位1,如 x8 + x2 + x + 1,二進(jìn)制為1 0000 0111,省略最高位1,轉(zhuǎn)換為十六進(jìn)制為0x07。
INIT:CRC初始值,和WIDTH位寬一致。
REFIN:true或false,在進(jìn)行計(jì)算之前,原始數(shù)據(jù)是否翻轉(zhuǎn),如原始數(shù)據(jù):0x34 = 0011 0100,如果REFIN為true,進(jìn)行翻轉(zhuǎn)之后為0010 1100 = 0x2c
REFOUT:true或false,運(yùn)算完成之后,得到的CRC值是否進(jìn)行翻轉(zhuǎn),如計(jì)算得到的CRC值:0x97 = 1001 0111,如果REFOUT為true,進(jìn)行翻轉(zhuǎn)之后為11101001 = 0xE9。
XOROUT:計(jì)算結(jié)果與此參數(shù)進(jìn)行異或運(yùn)算后得到最終的CRC值,和WIDTH位寬一致。
接收端的校驗(yàn)有兩種方式:
- 一種是和CRC計(jì)算一樣,在本地把接收到的數(shù)據(jù)和CRC分離,然后在本地對(duì)數(shù)據(jù)進(jìn)行CRC運(yùn)算,得到的CRC值和接收到的CRC進(jìn)行比較,如果一致,說明數(shù)據(jù)接收正確,如果不一致,說明數(shù)據(jù)有錯(cuò)誤。
- 另一種方法是把整個(gè)數(shù)據(jù)幀進(jìn)行CRC運(yùn)算,因?yàn)槭菙?shù)據(jù)幀相當(dāng)于把原始數(shù)據(jù)左移8位,然后加上余數(shù),如果直接對(duì)整個(gè)數(shù)據(jù)幀進(jìn)行CRC運(yùn)算(除以多項(xiàng)式),那么余數(shù)應(yīng)該為0,如果不為0說明數(shù)據(jù)出錯(cuò)
代碼計(jì)算
#include <stdio.h> #include <stdlib.h> #include <string.h> //求數(shù)的二進(jìn)制最高位的冪指數(shù),即MSB static int getMinPolynomialBits(uint64_t n) { int r = 0; while (n >>= 1) r++; return r; } //append>0表示計(jì)算crc校驗(yàn)碼,賦值到crcRemainder //append=0,表示校驗(yàn)輸入bit流是否正確;0表示正確,-1表示錯(cuò)誤 //此處的多項(xiàng)式默認(rèn)為0x96(高位補(bǔ)1后的結(jié)果),默認(rèn)crc位數(shù)為7,可根據(jù)代碼自行修改 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("%d\n", msgLen); //計(jì)算crc校驗(yàn)碼 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 { // 校驗(yàn)接受端的比特流 unsigned char* pBuffer = (unsigned char*)calloc(msgLen, sizeof(unsigned char)); memset(pBuffer, 0, msgLen); int inforLen = msgLen - polyLen;//提取出信息流部分,然后計(jì)算當(dāng)前信息對(duì)應(yīng)crc校驗(yàn)碼 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; //計(jì)算得到的crc碼和輸入的crc碼進(jìn)行對(duì)比驗(yàn)證,若每一位都相同,則校驗(yàn)成功 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; }
到此這篇關(guān)于C/C++實(shí)現(xiàn)crc碼計(jì)算和校驗(yàn)的文章就介紹到這了,更多相關(guān)C++ crc碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ 面向?qū)ο蟪绦蛟O(shè)計(jì)--內(nèi)存分區(qū)詳解
這篇文章主要介紹了剖析C++的面向?qū)ο缶幊趟枷?C++的面向?qū)ο筇匦允瞧鋵?duì)C語言的重要拓展之處,需要的朋友可以參考下,希望能夠給你帶來幫助2021-08-08C++構(gòu)造函數(shù)和析構(gòu)函數(shù)的使用與講解
今天小編就為大家分享一篇關(guān)于C++構(gòu)造函數(shù)和析構(gòu)函數(shù)的使用與講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12C語言實(shí)現(xiàn)循環(huán)隊(duì)列
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)循環(huán)隊(duì)列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07詳解C標(biāo)準(zhǔn)庫堆內(nèi)存函數(shù)
在C/C++語言中,我們知道內(nèi)存分為這幾種:程序全局變量內(nèi)存、棧內(nèi)存、堆內(nèi)存。其中堆內(nèi)存就是通過malloc(new)來分配的內(nèi)存,本文我們來探討一下C標(biāo)準(zhǔn)庫堆內(nèi)存函數(shù)。2021-06-06