整理好的PDF可以在BAIDU免费下载:http://wenku.baidu.com/view/fb791c0203d8ce2f006623f5.html我学习CRC32、CRC16、CRC原理和算法的总结(与WINRAR结果一致)wxleasyland([email protected])
2010年9月2日
比较愚钝,学了CRC校验好几天,很痛苦的过程,现终于有眉目了,总结一下。
国外版的“轻松无痛苦学习CRC指南”,在
http://www.repairfaq.org/filipg/LINK/F_crc_v31.html
(为什么好的资料都是老外写的?)我的英文有限,这种专业性太强的文章,很多都看不太明白,所以没办法翻译,靠参考国内的翻译和自己瞎琢磨的。
国内的翻译比较不全,而且有点误导,能看英文的还是看英文吧,国内版资料比较零散,可参考:
http://www.q.cc/2001/12/08/10190.html
http://www.360doc.com/content/10/0703/12/1317564_36621098.shtml
http://www.yuanma.org/data/2006/1010/article_1637.htm
我结合国内资料和英文原版进行总结,达到和WINRAR一样的CRC32计算结果。
一、 CRC原理
可参考http://www.luocong.com/articles/show_article.asp?Article_ID=15
计算CRC的过程,就是用一个特殊的“除法”,来得到余数,这个余数就是CRC。
它不是真正的算术上的除法!过程和算术除法过程一样,只是加减运算变成了XOR(异或)运算!
算术上的除法:
120÷9=13 余 3,120是被除数,9是除数,13是商,3是余数。念作120除以9,或者9除120,或者9去除120!(除法的过程就不写了)
这个除法计算机当然会做,但是做起来很麻烦,因为减法有借位,很耗时间和指令!
所以,计算CRC也是除法,但是用XOR来代替减法,这就简单多了!CRC的除法:
120÷9=14 余 6,商、余数和算术除法不一定相同!!因为除法用的是XOR,而不是真正的减法。
以二进制模拟这个计算过程:        1110        商为1110,即14,商有4位,表示进行了4次XOR
     ________
1001/1111000        被除数120是1111000,除数9是1001
     1001    ^
     ----
      1100     第一次XOR后得到011,加入下一位0。最高位的0可以消掉了,这样最高位是1,所以下个商是1
      1001    ^
      ----
       1010    第二次XOR后得到0101,加入下一位0。最高位的0可以消掉了,这样最高位是1,所以下个商是1
       1001    ^
       ----
        0110   第三次XOR后得到0011,加入下一位0。最高位的0可以消掉了,这样最高位是0,所以下个商是0
        0000    ^
        ----
         110 ->  最后一次XOR后得到0110,最高位的0可以消掉了,得到余数为110,即6
                 注意,余数不是0110,而是110,因为最前面那个0已经被XOR后消掉了!可见,除法(XOR)的目的是逐步消掉最高位的1或0!
由于过程是XOR的,所以商是没有意义的,我们不要。我们要的是余数。余数110是1111000的CRC吗?不是!
余数110是1111(即十进制15)的CRC!!!
为什么?因为CRC是和数据一起传送的,所以数据后面要加上CRC。
数据1111加上CRC110后,变成1111110,再传送。接收机收到1111110后,除以除数1001,余数为000,正确;如果余数不为0,则说明传送的数据有误!这样完成CRC校验。
即发送端要发送1111,先在1111后加000,变成1111000,再除以1001得到余数110,这个110就是CRC,将110加到数据后面,变成1111110,发送出去。
接收端收到1111110,用它除以1001,计算得余数为000,就说明收到的数据正确。
所以原始数据后面要先扩展出3位0,以容纳CRC值!
会发现,在上面的除法过程中,这3位0,能保证所有的4个数据位在除法时都能够被处理到!不然做一次除法就到结果了,那是不对的。这个概念后面要用到。所以,实际上,数据是1111,CRC是110。
对于除数1001,我们叫它生成多项式,即生成项,或POLY,即g(x)。
数据1111根据POLY1001,计算得到CRC110。
如果POLY不是1001,而是1011,那得到的CRC也是不同的!
所以生成项不同,得到的CRC也不同。要预先定义好POLY,发送端和接收端要用一样的POLY!
 二、 生成项
上面例子中,生成项是1001,共4位比特,最高位的1,实际上在除法的每次XOR时,都要消掉,所以这个1可不做参考,后3位001才是最重要的!001有3位,所以得到的余数也是3位,因为最后一次除法XOR时,最高位消掉了。所以CRC就是3位比特的。
CRC是3比特,表示它的宽度W=3。也就是说,原始数据后面要加上W=3比特的0进行扩展!
生成项的最低位也必须是1,这是规定的。
生成项1001,就等效于g(x)=x2+1
生成项也可以倒过来写,即颠倒过来,写成1001,这里倒过来的值是一样的。再如CRC32的生成项是:
1 0000 0100 1100 0001 0001 1101 1011 0111  (33个比特)
即g(x)= x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
颠倒过来,就可以写成1110 1101 1011 1000 1000 0011 0010 0000 1
一般生成项简写时不写最高位的1,故生成项是0x04C11DB7,颠倒后的生成项是0xEDB88320
CRC32的生成项是33比特,最高位是消掉的,即CRC值是32比特(4个字节),即宽度W=32,就是说,在计算前,原始数据后面要先扩展W=32个比特0,即4个0x00字节。注意:我看到网上CRC32的POLY有0x04C10DB7这个值的,它和正规的POLY值不同,需要注意!颠倒过来,即是镜像,为什么要颠倒,后述。 三、 直接计算法   Straightforward CRC Implementation“直接计算法”就是直接模拟上面的除法的过程,来得到余数即CRC!
上面的例子中,除数是4位,但最高位是要一直消掉的,所以我们只需要一个3位的寄存器就好了。
计算过程:
待测数据后扩展W=3个比特0,变成1111000;
寄存器初始化置0;
先在寄存器中移入数据111;
寄存器左移一位,并且右边移入下一位数据1。这样最高位1移出,由于最高位是1,故本次的商是1,要用除数1001来进行XOR,最高位肯定XOR得0,故不管它,只要用低3位001来进行XOR就可以,即001对寄存器进行XOR,寄存器中得到110,即第一次XOR后的结果(相当于是数据1111与生成项1001进行了一次XOR,并把最高位0消掉了)。 如果移出的最高位是0,则用0000来进行XOR(0 XOR 后,得到的还是原值)。
一直重复这个过程,就能得到最后余数了。总共处理次数=商的位数=待测数据的位数-生成项位数+1+宽度W=待测数据的位数=4次。我们假设待测数据是1101 0110 11,生成项是10011,假设有一个4 bits的寄存器,通过反复的移位和进行CRC的除法,最终该寄存器中的值就是我们所要求的余数。
             3   2   1   0   Bits
           +---+---+---+---+
 Pop   <-- |   |   |   |   | <----- Augmented message(已加0扩张的原始数据)
           +---+---+---+---+
        1    0   0   1   1   = The Poly 生成项依据这个模型,我们得到了一个最最简单的算法:
   把register中的值置0.
   把原始的数据后添加w个0.
   While (还有剩余没有处理的数据)
      Begin
      把register中的值左移一位,读入一个新的数据并置于register最低位的位置。
      If (如果上一步的左移操作中的移出的一位是1)
         register = register XOR Poly.
      End实际上就是模拟XOR除法的过程,即被测数据一位一位放到寄存器中来做除法。
比如生成项是10011,则生成的余数是4位XXXX,所以寄存器是4位。
待测数据是1101 0110 11,后面加上0000,即扩张4位,以容纳余数。
只要与生成项的0011做XOR就好了,最高位经过XOR肯定出0,可不用最高位。
过程如下:
待测数据先移4位即1101到寄存器中,准备开始除法。
第1次除法:寄存器中是1101,先从寄存器移出最高位1,移进下一位待测数据位0,则寄存器中是1010,由于移出的位是1,则需要与生成项的0011做XOR,得到1001,即做了第1次除法后,寄存器中是1001,这个就是余数。
第2次除法:寄存器中是1001,从寄存器移出最高位1,移进下一位待测数据位1,则寄存器中是0011,由于移出的位是1,则需要与生成项的0011做XOR,得到0000,即做了第2次除法后,寄存器中是0000,这个就是余数。
第3次除法:寄存器中是0000,从寄存器移出最高位0,移进下一位待测数据位1,则寄存器中是0001,由于移出的位是0,则需要不做XOR,直接下一步移位。也可以等同于:本次的商是0,0*生成项=0,即是0000与寄存器做XOR,得到寄存器的数不变,还是0001,即做了第3次除法后,寄存器中是0001,这个就是余数。
第4次除法:寄存器中是0001,从寄存器移出最高位0,移进下一位待测数据位0,则寄存器中是0010,由于移出的位是0,则需要不做XOR,直接下一步移位。
第5次除法:移位,不用做XOR,得到寄存器中是0101
第6次除法:移位,不用做XOR,得到寄存器中是1011
第7次除法:移位,移出的位是1,又要与生成项做XOR了
一直做下去直到最后,寄存器中的就是整个计算后的余数了。即CRC值。注意:
这个算法,计算出的CRC32值,与WINRAR计算出来的不一样,为什么?算法是正确的,不用怀疑!只是CRC32正式算法还涉及到数据颠倒和初始化预置值等,后述。程序实现:
程序1:
(注:网上下的程序是有错的,我有修改了,这里是正确的)   //网上的程序经修改
BYTE POLY=0x13; //生成项,13H=10011,这样CRC是4比特
unsigned short data = 0x035B; //待测数据是35BH,12比特,注意,数据不是16比特
unsigned short regi = 0x0000; // load the register with zero bits// augment the data by appending W(4) zero bits to the end of it.
//按CRC计算的定义,待测数据后加入4个比特0,以容纳4比特的CRC;
//这样共有16比特待测数据,从第5比特开始做除法,就要做16-5+1=12次XOR
data <<= 4;// we do it bit after bit
for ( int cur_bit = 15; cur_bit >= 0; -- cur_bit )   //处理16次,前4次实际上只是加载数据
{
// test the highest bit which will be poped later.
///     in fact, the 5th bit from right is the hightest bit here
    if ( ( ( regi >> 4 ) & 0x0001 ) == 0x1 )     regi = regi ^ POLY;    regi <<= 1;   // shift the register
// reading the next bit of the augmented data
    unsigned short tmp = ( data >> cur_bit ) & 0x0001; //加载待测数据1比特到tmp中,tmp只有1比特
    regi |= tmp; //这1比特加载到寄存器中
}
if ( ( ( regi >> 4 ) & 0x0001 ) == 0x1 )   regi = regi ^ POLY; //做最后一次XOR
//这时, regi中的值就是CRC
程序2:我做的通用CRC计算程序:_int64  POLY = 0x104C11DB7; //生成项,需要含有最高位的"1",这样CRC是32比特
int crcbitnumber=32; //crc是32比特_int64  data = 0x31323334; //待测数据,为字串"1234"
int  databitnumber=32; //数据是32比特_int64  regi = 0x0; // load the register with zero bits// augment the data by appending W zero bits to the end of it.
//按CRC计算的定义,加入32个比特0,以容纳32比特的CRC;
//这样共有64比特待测数据,从第33比特开始做除法,就要做64-33+1=32次XOR
data <<= crcbitnumber;// we do it bit after bit
for ( int cur_bit = databitnumber+crcbitnumber-1; cur_bit >= 0; -- cur_bit )   //处理64次(32比特待测数据+32比特扩展0),前32次是加载数据
{
// test the highest bit which will be poped later.
///     in fact, the 5th bit from right is the hightest bit here
    if ( ( ( regi >> crcbitnumber ) & 0x0001 ) == 0x1 )     regi = regi ^ POLY;    regi <<= 1;   // shift the register
// reading the next bit of the augmented data
    unsigned short tmp = ( data >> cur_bit ) & 0x0001; //加载待测数据1比特到tmp中,tmp只有1比特
    regi |= tmp; //这1比特加载到寄存器中
}
if ( ( ( regi >> crcbitnumber ) & 0x0001 ) == 0x1 )   regi = regi ^ POLY; //做最后一次XOR
//这时, regi中的值就是CRC 

解决方案 »

  1.   

    四、 驱动表法   Table-Driven Implementation上面的“直接计算法”很直观,却非常的低效。为了加快它的速度,我们使它一次能处理大于4 bit的数据。一次能处理一个字节的数据的话,那就方便多了。
    我们想要实现的32 bit的CRC校验。我们还是假设有和原来一样的一个4 "bit"的register,但它的每一位是一个8 bit的字节。
                 3    2    1    0   Bytes
              +----+----+----+----+
     Pop! <-- |    |    |    |    | <----- Augmented message(扩展0后的数据)
              +----+----+----+----+        1 <------32 bits------>  (生成项,暗含了一个最高位的“1”)根据同样的原理我们可以得到如下的算法:
      While (还有剩余没有处理的数据)
          Begin
          检查register头字节,并取得它的值
          求不同偏移处多项式的XOR
          register左移一个字节,最右处存入新读入的一个字节
          把register的值 和 多项式的XOR结果 进行XOR运算
          End可是为什么要这样作呢? 同样我们还是以一个简单的例子说明问题:
    为了简单起见,我们假设一次只移出4个比特!而不是8个比特。
    生成多项式为: 1 0101 1100,即宽度W=8,即CRC8,这样寄存器为8位
    待测数据是1011 0100 1101按正常的算法做:
    将1011 0100放入寄存器中,然后开始计算CRC。
    先将高4位移出寄存器:
    当前register中的值:        0100 1101
    4 bit应该被移出的值:  1011
    生成多项式为:          1010 1110 0
    第一步:
      Top  Register   (top指移出的数据)
      ---- --------
      1011 0100 1101               待测数
      1010 1110 0   + (CRC XOR)    POLY
      -------------
      0001 1010 1101               第一次XOR后的值
    第二步:
    这时,首4 bits 不为0说明没有除尽,要继续除:
      0001 1010 1101  
         1 0101 1100 + (CRC XOR)    将POLY右移3位后,再做XOR
      -------------
      0000 1111 0001                第二次XOR后的值
      ^^^^
    这时,首4 bits 全0说明不用继续除了,结果满足要求了。
    也就是说:待测数据与POLY相XOR,得到的结果再与POLY相XOR,POLY要适当移位,以消掉1。重复进行,直到结果满足要求。
    下面,我们换一种算法,来达到相同的目的:
    POLY与POLY自已先进行XOR,当然POLY要进行适当移位。使得得到的结果值的高4位与待测数据相同。
    第一步:
     1010 1110 0           POLY
        1 0101 1100 +      右移3位后的POLY
     -------------
     1011 1011 1100        POLY与POLY自已进行XOR后得到的值第二步:
     1011 1011 1100       POLY相XOR后得到的值
     1011 0100 1101+      待测数据
     -------------
     0000 11110001        得到的结果值和上面是一样的(说明可以先把POLY预先XOR好,再与待测数据XOR,就能得到结果)
    结论:
    现在我们看到,这二种算法计算的结果是一致的!这是基于XOR的交换律,即(a XOR b) XOR c = a XOR (b XOR c)。而后一种算法可以通过查表来快速完成,叫做“驱动表法”算法。
    也就是说,根据4 bit被移出的值1011,我们就可以知道要用POLY自身XOR后得到的 1011 1011 1100 来对待测数据1011 0100 1101进行XOR,这样一次就能消掉4BIT待测数据。(注意蓝色的最高4位要一样,这样XOR后才能得0000,就能消掉了)
    即1011对应1011 1011 1100,实际只需要用到后8位,即1011对应1011 1100
    用查表法来得到,即1011作为索引值,查表,得到表值1011 1100。
    表格可以预先生成。这里是每次移出4位,则POLY与POLY进行XOR的组合有2^4=16种,即从0000到1111。
    注意,POLY自身与自身相XOR时,要先对齐到和寄存器一样的长度,再XOR。相当于有12位进行XOR。
    组合后的结果有16种:(黑色的0表示对齐到和寄存器一样的长度)
    1. 0000 0000 0000        即表示待测数据移出的4位都是0,不需要与POLY相XOR,即相当于待测数据移出的4位后,与0000 0000 0000相XOR
    2. 0001 0101 1100   即表示待测数据移出的4位是0001,需要与右移过3位的POLY相XOR
    3. 0010 1011 1000  
    4. 0010 1011 1000 与 0001 0101 1100  相XOR,XOR后前4位为0011  即表示待测数据移出的4位后,需要与POLY进行二次相XOR,结果才能满足要求。
    5. 0101 0111 0000 与 0001 0101 1100相XOR, XOR后前4位为0100
    6. 0101 0111 0000 , 前4位为      0101
    7. 0101 0111 0000 与 0010 1011 1000、0001 0101 1100 相XOR, XOR后前4位为0110
    8. 0101 0111 0000 与 0010 1011 1000, XOR后前4位为0111
    9. 1010 1110 0000 与 0010 1011 1000相XOR, XOR后前4位为1000
    10. 1010 1110 0000 与 0010 1011 1000、0001 0101 1100相XOR, XOR后前4位为1001
    11. 1010 1110 0000, 前4位为      1010
    12. 1010 1110 0000 与 0001 0101 1100相XOR, XOR后前4位为1011
    13. 1010 1110 0000 与 0101 0111 0000、0010 1011 1000、0001 0101 1100相XOR, XOR后前4位为1100
    14. 1010 1110 0000 与 0101 0111 0000、0010 1011 1000相XOR, XOR后前4位为1101
    15. 1010 1110 0000 与 0101 0111 0000、0001 0101 1100相XOR, XOR后前4位为1110
    16. 1010 1110 0000 与 0101 0111 0000相XOR, XOR后前4位为1111以XOR后得到的结果的前4位做为索引值,以XOR后得到的结果的后8位做为表值,生成一张表,即:
    TABLE[0]=0000 0000B;
    TABLE[1]=0101 1100B;
    TABLE[2]=1011 1000B;
    TABLE[3]=[(0010 1011 1000B ^ 0001 0101 1100B) >> 4 ] & 0xff
    ....
    这张表我叫它为“直接查询表”。就是说,一次移出的待测数据的4位bit,有2^4个值,即0000,0001,0010,....,1111,根据这个值来查表,找到相应的表值,再用表值来XOR寄存器中的待测数据。所以,如果一次移出待测数据的8位bit,即一次进行一个字节的计算,则表格有2^8=256个表值。
    CRC16和CRC32都是一次处理一个字节的,所以它们的查询表有256个表值。“驱动表法”算法为:
                  3    2    1    0   Bytes
               +----+----+----+----+
        +-----<|    |    |    |    | <----- Augmented message(扩展0后的数据)
        |      +----+----+----+----+
        |      MSB       ^        LSB
        |                |
        |               XOR
        |                |
        |     0+----+----+----+----+
    查表v      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        +----->+----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+
            255+----+----+----+----+描述:
    1:register左移一个字节,从原始数据中读入一个新的字节. 
    2:利用刚从register移出的字节作为下标定位 table 中的一个32位的值 
    3:把这个值XOR到register中。
    4:如果还有未处理的数据则回到第一步继续执行。
    用C可以写成这样:
       r=0;            //r是寄存器,先初始化为0
       while (len--)   //len是已扩展0之后的待测数据的长度
         {
          byte t = (r >> 24) & 0xFF;
          r = (r << 8) | *p++;     //p是指向待测数据的指针(待测数据需已经扩展过0)
          r^=table[t];    //table是查询表
         }
    这个代码可以优化为:
      r=0;           //r是寄存器,先初始化为0
       while (len--) //len是已扩展0之后的待测数据的字节长度
              r = ((r << 8) | *p++) ^ t[(r >> 24) & 0xFF]; //p是指向待测数据的指针(待测数据需已经扩展过0),t是查询表注意:
    这个“驱动表法”算法和“直接计算法”是完全一样的,不仅结果完全一样,处理方式也是完全一样的,所以“驱动表法”可以完全替代“直接计算法”!
    原始数据都需要先用0扩展W位;最开始的几次循环的实质都只是先将待测数据移动到寄存器中去而已;
    会发现,这个算法用到的“直接查询表”的表值,和网上公开的查询表(我叫它“正规查询表”)的表值不一样!为什么?因为网上公开的正规查询表是用于“颠倒”算法的!后述。
    会发现,这个算法,计算出的CRC32值,同样与WINRAR计算出来的不一样。生成的“直接查询表”的内容是:
    CRC16直接查询表
    00H    0000  8005  800F  000A
    04H    801B  001E  0014  8011
    08H    8033  0036  003C  8039
    0CH    0028  802D  8027  0022
    10H    8063  0066  006C  8069
    14H    0078  807D  8077  0072
    18H    0050  8055  805F  005A
    1CH    804B  004E  0044  8041
    20H    80C3  00C6  00CC  80C9
    24H    00D8  80DD  80D7  00D2
    28H    00F0  80F5  80FF  00FA
    2CH    80EB  00EE  00E4  80E1
    30H    00A0  80A5  80AF  00AA
    34H    80BB  00BE  00B4  80B1
    38H    8093  0096  009C  8099
    3CH    0088  808D  8087  0082
    40H    8183  0186  018C  8189
    44H    0198  819D  8197  0192
    48H    01B0  81B5  81BF  01BA
    4CH    81AB  01AE  01A4  81A1
    50H    01E0  81E5  81EF  01EA
    54H    81FB  01FE  01F4  81F1
    58H    81D3  01D6  01DC  81D9
    5CH    01C8  81CD  81C7  01C2
    60H    0140  8145  814F  014A
    64H    815B  015E  0154  8151
    68H    8173  0176  017C  8179
    6CH    0168  816D  8167  0162
    70H    8123  0126  012C  8129
    74H    0138  813D  8137  0132
    78H    0110  8115  811F  011A
    7CH    810B  010E  0104  8101
    80H    8303  0306  030C  8309
    84H    0318  831D  8317  0312
    88H    0330  8335  833F  033A
    8CH    832B  032E  0324  8321
    90H    0360  8365  836F  036A
    94H    837B  037E  0374  8371
    98H    8353  0356  035C  8359
    9CH    0348  834D  8347  0342
    A0H    03C0  83C5  83CF  03CA
    A4H    83DB  03DE  03D4  83D1
    A8H    83F3  03F6  03FC  83F9
    ACH    03E8  83ED  83E7  03E2
    B0H    83A3  03A6  03AC  83A9
    B4H    03B8  83BD  83B7  03B2
    B8H    0390  8395  839F  039A
    BCH    838B  038E  0384  8381
    C0H    0280  8285  828F  028A
    C4H    829B  029E  0294  8291
    C8H    82B3  02B6  02BC  82B9
    CCH    02A8  82AD  82A7  02A2
    D0H    82E3  02E6  02EC  82E9
    D4H    02F8  82FD  82F7  02F2
    D8H    02D0  82D5  82DF  02DA
    DCH    82CB  02CE  02C4  82C1
    E0H    8243  0246  024C  8249
    E4H    0258  825D  8257  0252
    E8H    0270  8275  827F  027A
    ECH    826B  026E  0264  8261
    F0H    0220  8225  822F  022A
    F4H    823B  023E  0234  8231
    F8H    8213  0216  021C  8219
    FCH    0208  820D  8207  0202
      

  2.   

    CRC32直接查询表
    00H    00000000  04C11DB7  09823B6E  0D4326D9
    04H    130476DC  17C56B6B  1A864DB2  1E475005
    08H    2608EDB8  22C9F00F  2F8AD6D6  2B4BCB61
    0CH    350C9B64  31CD86D3  3C8EA00A  384FBDBD
    10H    4C11DB70  48D0C6C7  4593E01E  4152FDA9
    14H    5F15ADAC  5BD4B01B  569796C2  52568B75
    18H    6A1936C8  6ED82B7F  639B0DA6  675A1011
    1CH    791D4014  7DDC5DA3  709F7B7A  745E66CD
    20H    9823B6E0  9CE2AB57  91A18D8E  95609039
    24H    8B27C03C  8FE6DD8B  82A5FB52  8664E6E5
    28H    BE2B5B58  BAEA46EF  B7A96036  B3687D81
    2CH    AD2F2D84  A9EE3033  A4AD16EA  A06C0B5D
    30H    D4326D90  D0F37027  DDB056FE  D9714B49
    34H    C7361B4C  C3F706FB  CEB42022  CA753D95
    38H    F23A8028  F6FB9D9F  FBB8BB46  FF79A6F1
    3CH    E13EF6F4  E5FFEB43  E8BCCD9A  EC7DD02D
    40H    34867077  30476DC0  3D044B19  39C556AE
    44H    278206AB  23431B1C  2E003DC5  2AC12072
    48H    128E9DCF  164F8078  1B0CA6A1  1FCDBB16
    4CH    018AEB13  054BF6A4  0808D07D  0CC9CDCA
    50H    7897AB07  7C56B6B0  71159069  75D48DDE
    54H    6B93DDDB  6F52C06C  6211E6B5  66D0FB02
    58H    5E9F46BF  5A5E5B08  571D7DD1  53DC6066
    5CH    4D9B3063  495A2DD4  44190B0D  40D816BA
    60H    ACA5C697  A864DB20  A527FDF9  A1E6E04E
    64H    BFA1B04B  BB60ADFC  B6238B25  B2E29692
    68H    8AAD2B2F  8E6C3698  832F1041  87EE0DF6
    6CH    99A95DF3  9D684044  902B669D  94EA7B2A
    70H    E0B41DE7  E4750050  E9362689  EDF73B3E
    74H    F3B06B3B  F771768C  FA325055  FEF34DE2
    78H    C6BCF05F  C27DEDE8  CF3ECB31  CBFFD686
    7CH    D5B88683  D1799B34  DC3ABDED  D8FBA05A
    80H    690CE0EE  6DCDFD59  608EDB80  644FC637
    84H    7A089632  7EC98B85  738AAD5C  774BB0EB
    88H    4F040D56  4BC510E1  46863638  42472B8F
    8CH    5C007B8A  58C1663D  558240E4  51435D53
    90H    251D3B9E  21DC2629  2C9F00F0  285E1D47
    94H    36194D42  32D850F5  3F9B762C  3B5A6B9B
    98H    0315D626  07D4CB91  0A97ED48  0E56F0FF
    9CH    1011A0FA  14D0BD4D  19939B94  1D528623
    A0H    F12F560E  F5EE4BB9  F8AD6D60  FC6C70D7
    A4H    E22B20D2  E6EA3D65  EBA91BBC  EF68060B
    A8H    D727BBB6  D3E6A601  DEA580D8  DA649D6F
    ACH    C423CD6A  C0E2D0DD  CDA1F604  C960EBB3
    B0H    BD3E8D7E  B9FF90C9  B4BCB610  B07DABA7
    B4H    AE3AFBA2  AAFBE615  A7B8C0CC  A379DD7B
    B8H    9B3660C6  9FF77D71  92B45BA8  9675461F
    BCH    8832161A  8CF30BAD  81B02D74  857130C3
    C0H    5D8A9099  594B8D2E  5408ABF7  50C9B640
    C4H    4E8EE645  4A4FFBF2  470CDD2B  43CDC09C
    C8H    7B827D21  7F436096  7200464F  76C15BF8
    CCH    68860BFD  6C47164A  61043093  65C52D24
    D0H    119B4BE9  155A565E  18197087  1CD86D30
    D4H    029F3D35  065E2082  0B1D065B  0FDC1BEC
    D8H    3793A651  3352BBE6  3E119D3F  3AD08088
    DCH    2497D08D  2056CD3A  2D15EBE3  29D4F654
    E0H    C5A92679  C1683BCE  CC2B1D17  C8EA00A0
    E4H    D6AD50A5  D26C4D12  DF2F6BCB  DBEE767C
    E8H    E3A1CBC1  E760D676  EA23F0AF  EEE2ED18
    ECH    F0A5BD1D  F464A0AA  F9278673  FDE69BC4
    F0H    89B8FD09  8D79E0BE  803AC667  84FBDBD0
    F4H    9ABC8BD5  9E7D9662  933EB0BB  97FFAD0C
    F8H    AFB010B1  AB710D06  A6322BDF  A2F33668
    FCH    BCB4666D  B8757BDA  B5365D03  B1F740B4
    “驱动表法”的程序:// 注意:因生成项POLY最高位一定为“1”,故略去最高位的"1",
    unsigned short cnCRC_16 = 0x8005; // CRC-16 = X16 + X15 + X2 + X0 
    unsigned short cnCRC_CCITT = 0x1021; // CRC-CCITT = X16 + X12 + X5 + X0,据说这个 16 位 CRC 多项式比上一个要好 
    unsigned long cnCRC_32 = 0x04C11DB7; //采用正规的CRC32的POLY
    unsigned long Table_CRC16[256]; // CRC16 表 
    unsigned long Table_CRC32[256]; // CRC32 表 
    // 构造 16 位 CRC 表 "直接查询表"
    unsigned short i16, j16; 
    unsigned short nData16; 
    unsigned short nAccum16; 
    for ( i16 = 0; i16 < 256; i16++ ) 

    nData16 = ( unsigned short )( i16 << 8 ); 
    nAccum16 = 0; 
    for ( j16 = 0; j16 < 8; j16++ ) 

    if ( ( nData16 ^ nAccum16 ) & 0x8000 ) 
    nAccum16 = ( nAccum16 << 1 ) ^ cnCRC_16;   //也可以用cnCRC_CCITT
    else 
    nAccum16 <<= 1; 
    nData16 <<= 1; 

    Table_CRC16[i16] = ( unsigned long )nAccum16; 
    } // 构造 32 位 CRC 表 "直接查询表"
    unsigned long i32, j32; 
    unsigned long nData32; 
    unsigned long nAccum32; 
    for ( i32 = 0; i32 < 256; i32++ ) 

    nData32 = ( unsigned long )( i32 << 24 ); 
    nAccum32 = 0; 
    for ( j32 = 0; j32 < 8; j32++ ) 

    if ( ( nData32 ^ nAccum32 ) & 0x80000000 ) 
    nAccum32 = ( nAccum32 << 1 ) ^ cnCRC_32; 
    else 
    nAccum32 <<= 1; 
    nData32 <<= 1; 

    Table_CRC32[i32] = nAccum32; 

    unsigned char aData[512]={0x31,0x32,0x33,0x34}; //待测数据,为字串"1234"
    unsigned long aSize;
    unsigned long i; 
    unsigned char *point;// 计算 16 位 CRC 值,CRC-16 或 CRC-CCITT 
    //Table-Driven驱动表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据需扩展0
    unsigned short CRC16_1; 
    aSize=4; //数据长度字节(不包含扩展0)
    CRC16_1 = 0; //寄存器归0
    point=aData;
    while (aSize--)   
    CRC16_1 = ((CRC16_1 << 8) | *point++) ^ Table_CRC16[(CRC16_1 >> 8) & 0xFF];  
    for ( i = 0; i < 2; i++ )   
    CRC16_1 = ((CRC16_1 << 8) ) ^ Table_CRC16[(CRC16_1 >> 8) & 0xFF];  //加入2字节的扩展0
    //这时, CRC16_1中的值就是CRC// 计算 32 位 CRC-32 值 
    //Table-Driven驱动表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据需扩展0
    unsigned long CRC32_1; 
    aSize=4; //数据长度字节(不包含扩展0)
    CRC32_1=0x0; //寄存器归0
    point=aData;
    while (aSize--)   
    CRC32_1 = ((CRC32_1 << 8) | *point++) ^ Table_CRC32[(CRC32_1 >> 24) & 0xFF];  
    for ( i = 0; i < 4; i++ )   CRC32_1 = ((CRC32_1 << 8) ) ^ Table_CRC32[(CRC32_1 >> 24) & 0xFF];//加入4字节的扩展0
    //这时, CRC32_1中的值就是CRC打印查询表的语句:(在TC中实现)
    for ( i16 = 0; i16 < 256; i16++ ) 

    printf("%02xh    %04x  %04x  %04x  %04x\n",i16,( unsigned short)Table_CRC16[i16],( unsigned short)Table_CRC16[i16+1],( unsigned short)Table_CRC16[i16+2],( unsigned short)Table_CRC16[i16+3]);
    i16++;
    i16++;
    i16++;
    }for ( i16 = 0; i16 < 256; i16++ ) 

    printf("%02xh    %08lx  %08lx  %08lx  %08lx\n",i16,Table_CRC32[i16],Table_CRC32[i16+1],Table_CRC32[i16+2],Table_CRC32[i16+3]);
    i16++;
    i16++;
    i16++;

      

  3.   


    五、 直驱表法  DIRECT TABLE ALGORITHM对于上面的算法:
    1.对于尾部的w/8个扩展0字节,事实上它们的作用只是确保所有的原始数据都已被送入register,并且被算法处理。
    2.如果register中的初始值是0,那么开始的4次循环,作用只是把原始数据的头4个字节送入寄存器,而没有进行真正的除法操作。就算初始值不是0(我注:这里并没有说是“寄存器的初始值”,而是指算法开始时的“初始化值”),开始的4次循环也只是把原始数据的头4个字节移入到register中,然后再把它们和一个特定常数相XOR(我注:即这时进行初始化操作,后述)。
    3.因为有交换律:(A xor B) xor C = A xor (B xor C)
    这些信息意味着,上面提到的算法可以被优化,待测数据不需要先循环几次进入到寄存器中后再进行处理,而是数据直接就可以处理到寄存器中。
    可以这样:数据可以先与刚从寄存器移出的字节相XOR,用得到的结果值进行查表,再用表值XOR寄存器。这引出了以下“直驱表法”算法:
        +-----<Message (non augmented) 待测数据(不用扩展0)
        |
        v         3    2    1    0   Bytes
        |      +----+----+----+----+
       XOR----<|    |    |    |    |
        |      +----+----+----+----+
        |      MSB       ^        LSB
        |                |
        |               XOR
        |                |
        |     0+----+----+----+----+
    查表v      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        |      +----+----+----+----+
        +----->+----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+
            255+----+----+----+----+
    “直驱表法”算法:
    1. Shift the register left by one byte, reading in a new message byte.(我注:老外的这句话有问题,应只是Shift the register left by one byte,而不将新的信息字节读入register中。所以翻译为:寄存器左移一个字节)
    2. XOR the top byte just rotated out of the register with the next message byte to yield an index into the table ([0,255]). 将刚从register移出的字节与新的信息字节相XOR,结果值作为定位索引,从查询表中取得相应的表值。
    3. XOR the table value into the register. 把表值XOR到register中
    4. Goto 1 iff more augmented message bytes. 如果还有未处理的数据则回到第一步继续执行。
    用C可以写成这样:
       r=0; //r是寄存器,先初始化为0
       while (len--) //len是待测数据(不用扩展0)的字节长度
              r = (r<<8) ^ t[(r >> 24) ^ *p++];  //p是指向待测数据的指针,t是查询表
    算法相当于:
    寄存器左移出1字节,右边补0;
    移出的字节与待测信息字节进行XOR,根据结果值查表,得表值
    表值与寄存器进行XOR注意:
    这个“直驱表法”算法的数学原理我也不明白,但它肯定是从“驱动表法”算法推导出来的,得到的CRC结果值是完全一样的!只是它们的处理方式是不一样的。
    这个算法很方便,原始数据不需要先用0扩展W位;
    这个算法很方便,第一次循环就能够处理待测数据并在寄存器中生成结果,而不单纯只是将数据移动到寄存器中去而已;
    这个算法,用的是和“驱动表法”同样的“直接查询表”,而不是“正规查询表”。
    正是由于处理方式不一样,所以如果寄存器初始化预置值不为0,那么本算法可不受影响,而“驱动表法”则需要将预置值再另外XOR到寄存器中。后述。“直驱表法”的程序:// 注意:因生成项POLY最高位一定为“1”,故略去最高位的"1",
    unsigned short cnCRC_16 = 0x8005; // CRC-16 = X16 + X15 + X2 + X0 
    unsigned short cnCRC_CCITT = 0x1021; // CRC-CCITT = X16 + X12 + X5 + X0,据说这个 16 位 CRC 多项式比上一个要好 
    unsigned long cnCRC_32 = 0x04C11DB7; //采用正规的CRC32的POLY
    unsigned long Table_CRC16[256]; // CRC16 表 
    unsigned long Table_CRC32[256]; // CRC32 表 
    // 构造 16 位 CRC 表 "直接查询表"
    unsigned short i16, j16; 
    unsigned short nData16; 
    unsigned short nAccum16; 
    for ( i16 = 0; i16 < 256; i16++ ) 

    nData16 = ( unsigned short )( i16 << 8 ); 
    nAccum16 = 0; 
    for ( j16 = 0; j16 < 8; j16++ ) 

    if ( ( nData16 ^ nAccum16 ) & 0x8000 ) 
    nAccum16 = ( nAccum16 << 1 ) ^ cnCRC_16;   //也可以用cnCRC_CCITT
    else 
    nAccum16 <<= 1; 
    nData16 <<= 1; 

    Table_CRC16[i16] = ( unsigned long )nAccum16; 
    } // 构造 32 位 CRC 表 "直接查询表"
    unsigned long i32, j32; 
    unsigned long nData32; 
    unsigned long nAccum32; 
    for ( i32 = 0; i32 < 256; i32++ ) 

    nData32 = ( unsigned long )( i32 << 24 ); 
    nAccum32 = 0; 
    for ( j32 = 0; j32 < 8; j32++ ) 

    if ( ( nData32 ^ nAccum32 ) & 0x80000000 ) 
    nAccum32 = ( nAccum32 << 1 ) ^ cnCRC_32; 
    else 
    nAccum32 <<= 1; 
    nData32 <<= 1; 

    Table_CRC32[i32] = nAccum32; 

    unsigned char aData[512]={0x31,0x32,0x33,0x34}; //待测数据,为字串"1234"
    unsigned long aSize;
    unsigned long i; 
    unsigned char *point;// 计算 16 位 CRC 值,CRC-16 或 CRC-CCITT 
    //DIRECT TABLE直驱表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据不需要扩展0
    unsigned short CRC16_2; 
    aSize=4; //数据长度字节(数据不用扩展0了)
    CRC16_2 = 0; //寄存器中预置初始值
    point=aData;
    for ( i = 0; i < aSize; i++ ) 
    CRC16_2 = ( CRC16_2 << 8 ) ^ ( unsigned short ) Table_CRC16[( CRC16_2 >> 8 ) ^ *point++]; 
    //这时, CRC16_2中的值就是CRC
    // 计算 32 位 CRC-32 值 
    //DIRECT TABLE直驱表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据不需要扩展0
    unsigned long CRC32_2; 
    aSize=4; //数据长度字节(数据不用扩展0了)
    CRC32_2 = 0x0; //寄存器中预置初始值
    point=aData;
    for ( i = 0; i < aSize; i++ ) 
    CRC32_2 = ( CRC32_2 << 8 ) ^ Table_CRC32[( CRC32_2 >> 24 ) ^ *point++]; 
    //这时, CRC32_2中的值就是CRC
     
      

  4.   


     
    六、 CRC的参数模型实际上,这时已经可以计算出与WINRAR相同的CRC32值了。但是会发现,算出的结果和WINRAR是不一样的,为什么?因为不仅仅是生成项POLY会影响到CRC值,还有很多参数会影响到最终的CRC值!
    CRC计算,需要有CRC参数模型,比如CRC32的参数模型是:
       Name   : "CRC-32"
       Width  : 32
       Poly   : 04C11DB7
       Init   : FFFFFFFF
       RefIn  : True
       RefOut : True
       XorOut : FFFFFFFF
       Check  : CBF43926
    解释:
    NAME 
    名称
    WIDTH 
    宽度,即CRC比特数
    POLY 
    生成项的简写。以16进制表示,即是0x04C11DB7。忽略了最高位的"1",即完整的生成项是0x104C11DB7。
    重要的一点是,这是“未颠倒”的生成项!前面说过,“颠倒的”生成项是0xEDB88320。INIT 
    这是算法开始时寄存器的初始化预置值,十六进制表示。
    这个值可以直接赋值给“直驱表法”算法中的寄存器,作为寄存器的初始值!
    而对于“驱动表法”算法及“直接计算法”,寄存器的初始值必须是0!前面几次循环先将待测数据移入到寄存器中,当寄存器装满后,再用这个初始化预置值去XOR寄存器,这样寄存器就被这个值初始化了!
    这点很重要!!如果在“驱动表法”算法开始时,寄存器的初始值不为0,那么寄存器中的值就会相当于是待测数据了,这样算出的CRC结果就不对了!我们的目的是用预置值去初始化寄存器,而不是将预置值作为待测数据去处理!
    REFIN 
    这个值是真TRUE或假FALSE。
    如果这个值是FALSE,表示待测数据的每个字节都不用“颠倒”,即BIT7仍是作为最高位,BIT0作为最低位。
    如果这个值是TRUE,表示待测数据的每个字节都要先“颠倒”,即BIT7作为最低位,BIT0作为最高位。
    REFOUT 
    这个值是真TRUE或假FALSE。
    如果这个值是FALSE,表示计算结束后,寄存器中的值直接进入XOROUT处理即可。
    如果这个值是TRUE,表示计算结束后,寄存器中的值要先“颠倒”,再进入XOROUT处理。注意,这是将整个寄存器的值颠倒,因为寄存器的各个字节合起来表达了一个值,如果只是对各个字节各自颠倒,那结果值就错误了。
    XOROUT 
    这是W位长的16进制数值。
    这个值与经REFOUT后的寄存器的值相XOR,得到的值就是最终正式的CRC值!
    CHECK 
    这不是定义值的一部分,这只是字串"123456789"用这个CRC参数模型计算后得到的CRC值,作为参考。我们发现,CRC32模型的Init=0xFFFFFFFF,就是说寄存器要用0xFFFFFFFF进行初始化,而不是0。
    为什么?因为待测数据的内容和长度是随机的,如果寄存器初始值为0,那么,待测字节是1字节的0x00,与待测字节是N字节的0x00,计算出来的CRC32值都是0,那CRC值就没有意义了!所以寄存器用0xFFFFFFFF进行初始化,就可以避免这个问题了!RefIn=True,表示输入数据的每个字节需要“颠倒”!为什么要“颠倒”,因为很多硬件在发送时是先发送最低位LSB的!比如UART等。
    字节顺序不用颠倒,只是每个字节内部的比特进行颠倒。例如待测的字串是"1234",这时也是一样先处理"1",再处理"2",一直到处理"4"。处理字符"1"时,它是0x31,即0011 0001,需要先将它颠倒,变成低位在前,即1000 1100,即0x8C,再进行处理。
    也就是说,待处理的数据是0x31 32 33 34,颠倒后就变成0x8C 4C CC 2C,再进行CRC计算。RefOut=True,表示计算完成后,要将寄存器中的值再颠倒。注意,这是将整个寄存器的值颠倒,即如果寄存器中的值是0x31 32 33 34,颠倒后就变成0x2C CC 4C 8C!XorOut=FFFFFFFF,表示还需要将结果值与0xffffffff进行XOR,这样就得到最终的CRC32值了!我们直接用“直驱表法”,计算字串"1234"的CRC32值。程序如下:
    要先做一个颠倒比特的子程序:
    unsigned long int Reflect(unsigned long int ref, char ch) 
    {
    unsigned long int value=0; 
    // 交换bit0和bit7,bit1和bit6,类推 
    for(int i = 1; i < (ch + 1); i++) 

    if(ref & 1) 
    value |= 1 << (ch - i); 
    ref >>= 1; 

    return value; 
    }在主程序中的程序:
    // 注意:因生成项POLY最高位一定为“1”,故略去最高位的"1",
    unsigned long cnCRC_32 = 0x04C11DB7; //采用正规的CRC32的POLY
    unsigned long Table_CRC32[256]; // CRC32 表 // 构造 32 位 CRC 表 "直接查询表"
    unsigned long i32, j32; 
    unsigned long nData32; 
    unsigned long nAccum32; 
    for ( i32 = 0; i32 < 256; i32++ ) 

    nData32 = ( unsigned long )( i32 << 24 ); 
    nAccum32 = 0; 
    for ( j32 = 0; j32 < 8; j32++ ) 

    if ( ( nData32 ^ nAccum32 ) & 0x80000000 ) 
    nAccum32 = ( nAccum32 << 1 ) ^ cnCRC_32; 
    else 
    nAccum32 <<= 1; 
    nData32 <<= 1; 

    Table_CRC32[i32] = nAccum32; 
    } unsigned char aData[512]={0x31,0x32,0x33,0x34}; //待测数据,为字串"1234"
    unsigned long aSize;
    unsigned long i; 
    unsigned char *point;
    unsigned char chtemp;
    // 计算 32 位 CRC-32 值 
    //Table-Driven驱动表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据需扩展0
    unsigned long ii; 
    unsigned long CRC32_1; 
    aSize=4; //数据长度字节(不包含扩展0)
    CRC32_1=0x0; //寄存器归0
    point=aData;
    ii=0;
    while (aSize--)   
    {
    chtemp=*point++;
    chtemp=(unsigned char)Reflect(chtemp, 8); //将数据字节内部的比特进行颠倒
    CRC32_1 = ((CRC32_1 << 8) | chtemp) ^ Table_CRC32[(CRC32_1 >> 24) & 0xFF];  
    ii++;
    if (ii==4) CRC32_1=CRC32_1^0xffffffff;//当寄存器装满4个字节后,用预置值0xffffffff去XOR寄存器,这样寄存器就被这个值初始化了!
    }
    for ( i = 0; i < 4; i++ )   
    {
    CRC32_1 = ((CRC32_1 << 8) ) ^ Table_CRC32[(CRC32_1 >> 24) & 0xFF];  //加入4字节的扩展0
    ii++;
    if (ii==4) CRC32_1=CRC32_1^0xffffffff;//如果待测数据小于4字节,则只有在这里寄存器才会装满4个字节,才进行初始化
    }
    CRC32_1=Reflect(CRC32_1, 32); //颠倒寄存器的值
    CRC32_1=CRC32_1^0xffffffff; //寄存器的值与0xffffffff异或
    //这时, CRC32_1中的值就是CRC
    //DIRECT TABLE直驱表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据不需要扩展0
    unsigned long CRC32_2; 
    aSize=4; //数据长度字节(数据不用扩展0了)
    CRC32_2 = 0xffffffff; //寄存器中直接预置初始值0xffffffff即可
    point=aData;
    for ( i = 0; i < aSize; i++ ) 
    {
    chtemp=*point++;
    chtemp=(unsigned char)Reflect(chtemp, 8); //将数据字节内部的比特进行颠倒
    CRC32_2 = ( CRC32_2 << 8 ) ^ Table_CRC32[( CRC32_2 >> 24 ) ^ chtemp];
    }CRC32_2=Reflect(CRC32_2, 32); //颠倒寄存器的值
    CRC32_2=CRC32_2^0xffffffff;     //寄存器的值与0xffffffff异或
    //这时, CRC32_2中的值就是CRC
    得到的结果与WINRAR的计算结果是完全一样的!成功了!其它的CRC参数模型:
       Name   : "CRC-16"
       Width  : 16
       Poly   : 8005
       Init   : 0000
       RefIn  : True
       RefOut : True
       XorOut : 0000
       Check  : BB3D   Name   : "CRC-16/CITT"
       Width  : 16
       Poly   : 1021
       Init   : FFFF
       RefIn  : False
       RefOut : False
       XorOut : 0000
       Check  : ?   Name   : "XMODEM"
       Width  : 16
       Poly   : 8408
       Init   : 0000
       RefIn  : True
       RefOut : True
       XorOut : 0000
       Check  : ?   Name   : "ARC"
       Width  : 16
       Poly   : 8005
       Init   : 0000
       RefIn  : True
       RefOut : True
       XorOut : 0000
       Check  : ? 
      

  5.   

    七、 最后的战斗-“颠倒的直驱表法”算法 "Reflected" Table-Driven Implementations颠倒,也就是镜像!
    CRC32要求输入的字节要颠倒,那么在程序中,在对每个字节处理前,还要先把这个字节先颠倒一下,再处理,那不是超级麻烦!
    所以就把“直驱表法”算法颠倒一下(查询表颠倒),那么算法就可以直接处理不颠倒的字节了,就方便多了。
    我们把算法照镜子:         “直驱表法”                    镜子               “颠倒的直驱表法”    +-----<Message (non augmented) 待测数据(要颠倒)
        |
        v         3    2    1    0   Bytes
        |      +----+----+----+----+
       XOR----<|    |    |    |    |
        |      +----+----+----+----+
        |      MSB       ^        LSB
        |                |
        |               XOR
        |                |
        |   00H +----+----+----+----+
    查表v  01H +----+----+----+----+ 04C11DB7H
        |   02H +----+----+----+----+
        |   03H +----+----+----+----+
        |       +----+----+----+----+
        |       +----+----+----+----+
        |       +----+----+----+----+
        +-----> +----+----+----+----+
                +----+----+----+----+
            80H +----+----+----+----+ 690CE0EEH
                +----+----+----+----+
                +----+----+----+----+
            FFH +----+----+----+----+
              索引值和表值都不颠倒
    待测数据(不颠倒)Message (non augmented) >-----+
                                          |
          Bytes   3    2    1    0        v
               +----+----+----+----+      |
               |    |    |    |    |>----XOR
               +----+----+----+----+      |
               MSB       ^        LSB     |
                         |                |
                        XOR               |
                         |                |
               +----+----+----+----+ 00H  |
     EDB88320H +----+----+----+----+ 80H  v查表
               +----+----+----+----+ C0H  |
               +----+----+----+----+ 20H  |
               +----+----+----+----+ A0H  |
               +----+----+----+----+      |
               +----+----+----+----+      |
               +----+----+----+----+<-----+
               +----+----+----+----+
    77073096H +----+----+----+----+ 01H
               +----+----+----+----+
               +----+----+----+----+
               +----+----+----+----+255
                          索引值和表值都颠倒
    “颠倒的直驱表法”用的查询表,是网上可以找到的查询表,我叫它“正规查询表”,实际就是“颠倒的直接查询表”。对应关系是:
    “直接查询表”                            “正规查询表”(颠倒的查询表)
    0000 0000(00H)                         0000 0000(00H)
    0000 0001(01H)表值04C11DB7H           1000 0000(80H)表值EDB88320H
    0000 0010(02H)                         0100 0000(C0H)
    0000 0011(03H)                         1100 0000(20H)
    0000 0100(04H)                         0010 0000(A0H)
    ...
    1000 0000(80H)表值690CE0EEH            0000 0001(01H)表值77073096H
    ...
    1111 1111(FFH)                          FFFF FFFF(FFH)也就是说,将“直接查询表”的索引值和表值直接镜像就是“正规查询表”。
    比如,直接查询表的[01H]= 04C11DB7H,因为01H镜像后是80H,04C11DB7H镜像后是EDB88320H,就得到正规查询表的[80H]= EDB88320H。举例来说,假设待测的原始数据是10H,简单起见,不考虑寄存器移出的字节的影响(即假设它是00H):
    “直驱表法”,原始数据先颠倒为01H,根据01H查表得04C11DB7H,寄存器移出的字节是向左移。
    “颠倒的直驱表法”,    直接根据原始数据10H查表得EDB88320H,寄存器移出的字节是向右移。
    可见,这时这二个方法本质上是一样的。对于“直驱表法”,颠倒的数据用不颠倒的表索引值、得到不颠倒的表值寄存器进入寄存器,得到的寄存器结果值是不颠倒的,还要再颠倒,变成“颠倒的CRC”。
    对于“颠倒的直驱表法”,不颠倒的数据用颠倒的表索引值、得到颠倒的表值寄存器进入寄存器,得到的寄存器结果值就已经是“颠倒的CRC”,不用再颠倒了。
    可见,对于REFIN=TRUE并且REFOUT=TRUE的CRC模型来说(注意这个先决条件),就可以直接用“颠倒的直驱表法”来代替“直驱表法”,这样原始数据的比特不用镜像,处理起来就很简单。那是不是说:不颠倒的数据用不颠倒的表索引值和表值,把得到的寄存器结果值颠倒一下,就能得到和颠倒的数据一样的计算结果?不可能的。颠倒的数据和不颠倒的数据是完全不一样的数据,得到的结果完全两码事。注意:
    先决条件是REFIN=TRUE并且REFOUT=TRUE的CRC参数模型。
    “颠倒的直驱表法”用的是“正规查询表”。
    寄存器的初始化预置值也要颠倒。
    待测数据每个字节的比特不用颠倒,因为算法的其他部分都做过颠倒处理了。
    待测数据串肯定不用颠倒,即待测的字串是"1234",这时也是一样先处理"1",再处理"2",一直到处理"4"。算法如下:
    1. 将寄存器向右移动一个字节。
    2. 将刚移出的那个字节与待测数据中的新字节做XOR运算,得到一个指向查询表的索引值。
    3. 将索引所指的表值与寄存器做XOR运算。
    4. 如数据没有全部处理完,则跳到步骤1。用C可以写成这样:
       r=0; //r是寄存器,先初始化为0
    for(i=0;  i <len;  i++)   //len是待测数据(不用扩展0)的字节长度

    r = t[( r^(*(p+i)) ) & 0xff] ^ (r >> 8);  //p是指向待测数据的指针,t是查询表
    }“正规查询表”的内容是:
    CRC16正规查询表
      00h   0000 C0C1 C181 0140 C301 03C0 0280 C241
      08h   C601 06C0 0780 C741 0500 C5C1 C481 0440
      10h   CC01 0CC0 0D80 CD41 0F00 CFC1 CE81 0E40
      18h   0A00 CAC1 CB81 0B40 C901 09C0 0880 C841
      20h   D801 18C0 1980 D941 1B00 DBC1 DA81 1A40
      28h   1E00 DEC1 DF81 1F40 DD01 1DC0 1C80 DC41
      30h   1400 D4C1 D581 1540 D701 17C0 1680 D641
      38h   D201 12C0 1380 D341 1100 D1C1 D081 1040
      40h   F001 30C0 3180 F141 3300 F3C1 F281 3240
      48h   3600 F6C1 F781 3740 F501 35C0 3480 F441
      50h   3C00 FCC1 FD81 3D40 FF01 3FC0 3E80 FE41
      58h   FA01 3AC0 3B80 FB41 3900 F9C1 F881 3840
      60h   2800 E8C1 E981 2940 EB01 2BC0 2A80 EA41
      68h   EE01 2EC0 2F80 EF41 2D00 EDC1 EC81 2C40
      70h   E401 24C0 2580 E541 2700 E7C1 E681 2640
      78h   2200 E2C1 E381 2340 E101 21C0 2080 E041
      80h   A001 60C0 6180 A141 6300 A3C1 A281 6240
      88h   6600 A6C1 A781 6740 A501 65C0 6480 A441
      90h   6C00 ACC1 AD81 6D40 AF01 6FC0 6E80 AE41
      98h   AA01 6AC0 6B80 AB41 6900 A9C1 A881 6840
      A0h   7800 B8C1 B981 7940 BB01 7BC0 7A80 BA41
      A8h   BE01 7EC0 7F80 BF41 7D00 BDC1 BC81 7C40
      B0h   B401 74C0 7580 B541 7700 B7C1 B681 7640
      B8h   7200 B2C1 B381 7340 B101 71C0 7080 B041
      C0h   5000 90C1 9181 5140 9301 53C0 5280 9241
      C8h   9601 56C0 5780 9741 5500 95C1 9481 5440
      D0h   9C01 5CC0 5D80 9D41 5F00 9FC1 9E81 5E40
      D8h   5A00 9AC1 9B81 5B40 9901 59C0 5880 9841
      E0h   8801 48C0 4980 8941 4B00 8BC1 8A81 4A40
      E8h   4E00 8EC1 8F81 4F40 8D01 4DC0 4C80 8C41
      F0h   4400 84C1 8581 4540 8701 47C0 4680 8641
      F8h   8201 42C0 4380 8341 4100 81C1 8081 4040
    CRC32正规查询表
      00h   00000000 77073096 EE0E612C 990951BA
      04h   076DC419 706AF48F E963A535 9E6495A3
      08h   0EDB8832 79DCB8A4 E0D5E91E 97D2D988
      0Ch   09B64C2B 7EB17CBD E7B82D07 90BF1D91
      10h   1DB71064 6AB020F2 F3B97148 84BE41DE
      14h   1ADAD47D 6DDDE4EB F4D4B551 83D385C7
      18h   136C9856 646BA8C0 FD62F97A 8A65C9EC
      1Ch   14015C4F 63066CD9 FA0F3D63 8D080DF5
      20h   3B6E20C8 4C69105E D56041E4 A2677172
      24h   3C03E4D1 4B04D447 D20D85FD A50AB56B
      28h   35B5A8FA 42B2986C DBBBC9D6 ACBCF940
      2Ch   32D86CE3 45DF5C75 DCD60DCF ABD13D59
      30h   26D930AC 51DE003A C8D75180 BFD06116
      34h   21B4F4B5 56B3C423 CFBA9599 B8BDA50F
      38h   2802B89E 5F058808 C60CD9B2 B10BE924
      3Ch   2F6F7C87 58684C11 C1611DAB B6662D3D
      40h   76DC4190 01DB7106 98D220BC EFD5102A
      44h   71B18589 06B6B51F 9FBFE4A5 E8B8D433
      48h   7807C9A2 0F00F934 9609A88E E10E9818
      4Ch   7F6A0DBB 086D3D2D 91646C97 E6635C01
      50h   6B6B51F4 1C6C6162 856530D8 F262004E
      54h   6C0695ED 1B01A57B 8208F4C1 F50FC457
      58h   65B0D9C6 12B7E950 8BBEB8EA FCB9887C
      5Ch   62DD1DDF 15DA2D49 8CD37CF3 FBD44C65
      60h   4DB26158 3AB551CE A3BC0074 D4BB30E2
      64h   4ADFA541 3DD895D7 A4D1C46D D3D6F4FB
      68h   4369E96A 346ED9FC AD678846 DA60B8D0
      6Ch   44042D73 33031DE5 AA0A4C5F DD0D7CC9
      70h   5005713C 270241AA BE0B1010 C90C2086
      74h   5768B525 206F85B3 B966D409 CE61E49F
      78h   5EDEF90E 29D9C998 B0D09822 C7D7A8B4
      7Ch   59B33D17 2EB40D81 B7BD5C3B C0BA6CAD
      80h   EDB88320 9ABFB3B6 03B6E20C 74B1D29A
      84h   EAD54739 9DD277AF 04DB2615 73DC1683
      88h   E3630B12 94643B84 0D6D6A3E 7A6A5AA8
      8Ch   E40ECF0B 9309FF9D 0A00AE27 7D079EB1
      90h   F00F9344 8708A3D2 1E01F268 6906C2FE
      94h   F762575D 806567CB 196C3671 6E6B06E7
      98h   FED41B76 89D32BE0 10DA7A5A 67DD4ACC
      9Ch   F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5
      A0h   D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252
      A4h   D1BB67F1 A6BC5767 3FB506DD 48B2364B
      A8h   D80D2BDA AF0A1B4C 36034AF6 41047A60
      ACh   DF60EFC3 A867DF55 316E8EEF 4669BE79
      B0h   CB61B38C BC66831A 256FD2A0 5268E236
      B4h   CC0C7795 BB0B4703 220216B9 5505262F
      B8h   C5BA3BBE B2BD0B28 2BB45A92 5CB36A04
      BCh   C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D
      C0h   9B64C2B0 EC63F226 756AA39C 026D930A
      C4h   9C0906A9 EB0E363F 72076785 05005713
      C8h   95BF4A82 E2B87A14 7BB12BAE 0CB61B38
      CCh   92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21
      D0h   86D3D2D4 F1D4E242 68DDB3F8 1FDA836E
      D4h   81BE16CD F6B9265B 6FB077E1 18B74777
      D8h   88085AE6 FF0F6A70 66063BCA 11010B5C
      DCh   8F659EFF F862AE69 616BFFD3 166CCF45
      E0h   A00AE278 D70DD2EE 4E048354 3903B3C2
      E4h   A7672661 D06016F7 4969474D 3E6E77DB
      E8h   AED16A4A D9D65ADC 40DF0B66 37D83BF0
      ECh   A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9
      F0h   BDBDF21C CABAC28A 53B39330 24B4A3A6
      F4h   BAD03605 CDD70693 54DE5729 23D967BF
      F8h   B3667A2E C4614AB8 5D681B02 2A6F2B94
      FCh   B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D“颠倒的直驱表法”的程序:同样要先做一个颠倒比特的子程序:
    unsigned long int Reflect(unsigned long int ref, char ch) 
    {
    unsigned long int value=0; 
    // 交换bit0和bit7,bit1和bit6,类推 
    for(int i = 1; i < (ch + 1); i++) 

    if(ref & 1) 
    value |= 1 << (ch - i); 
    ref >>= 1; 

    return value; 
    }在主程序中的程序:
    unsigned long int crc32_table[256]; 
    unsigned long int ulPolynomial = 0x04c11db7; 
    unsigned long int crc,temp; for(int i = 0; i <= 0xFF; i++)   // 生成CRC32“正规查询表”

    temp=Reflect(i, 8); 
    crc32_table[i]= temp<< 24; 
    for (int j = 0; j < 8; j++)

    unsigned long int t1,t2; 
    unsigned long int flag=crc32_table[i]&0x80000000; 
    t1=(crc32_table[i] << 1); 
    if(flag==0) 
    t2=0; 
    else 
    t2=ulPolynomial; 
    crc32_table[i] =t1^t2 ; 

    crc=crc32_table[i]; 
    crc32_table[i] = Reflect(crc32_table[i], 32); 

       //计算CRC32值
    unsigned   long   CRC32;
    BYTE  DataBuf[512]={0x31,0x32,0x33,0x34}; //待测数据,为字串"1234"
    unsigned   long   len;
    unsigned   long   ii; 
    unsigned   long   m_CRC = 0xFFFFFFFF; //寄存器中预置初始值
    BYTE   *p; len=4;             //待测数据的字节长度
    p = DataBuf; 
    for(ii=0;  ii <len;  ii++) 

    m_CRC = crc32_table[( m_CRC^(*(p+ii)) ) & 0xff] ^ (m_CRC >> 8);  //计算

    CRC32= ~m_CRC;     //取反。经WINRAR对比,CRC32值正确!!
    //这时, CRC32中的值就是CRC 
      

  6.   


    八、 结束
    以上程序均在VC6中测试成功,很多程序是直接抄网上的再修改。
    CRC计算其实很简单,也就才4个算法,移来移去、优化来优化去而已。
    但是就是这么简单的东西,国内网上好像没有文章能说得完全明白。搞得我这种笨人花了整整5天才整理出来。
    书上的都是理论一大堆,这个式子那个式子的,一头雾水,我这个非专业人士更看不懂。
    应该说,用程序实现和优化算法是容易的,创造出CRC方法的人才是真正的强人。
      

  7.   

    ,以后肯定用得到,哈哈,其实我是直接收藏了,crc当年学的时候就觉得困难,现在有现成的了,幸福哎~
      

  8.   

    http://blog.csdn.net/blackboyofsnp/archive/2008/01/14/2043934.aspx
      

  9.   

    CRC32直接查询表
    00H 00000000 04C11DB7 09823B6E 0D4326D9
    04H 130476DC 17C56B6B 1A864DB2 1E475005
    08H 2608EDB8 22C9F00F 2F8AD6D6 2B4BCB61
    0CH 350C9B64 31CD86D3 3C8EA00A 384FBDBD
    10H 4C11DB70 48D0C6C7 4593E01E 4152FDA9
    14H 5F15ADAC 5BD4B01B 569796C2 52568B75
    18H 6A1936C8 6ED82B7F 639B0DA6 675A1011
    1CH 791D4014 7DDC5DA3 709F7B7A 745E66CD
    20H 9823B6E0 9CE2AB57 91A18D8E 95609039
    24H 8B27C03C 8FE6DD8B 82A5FB52 8664E6E5
    28H BE2B5B58 BAEA46EF B7A96036 B3687D81
    2CH AD2F2D84 A9EE3033 A4AD16EA A06C0B5D
    30H D4326D90 D0F37027 DDB056FE D9714B49
    34H C7361B4C C3F706FB CEB42022 CA753D95
    38H F23A8028 F6FB9D9F FBB8BB46 FF79A6F1
    3CH E13EF6F4 E5FFEB43 E8BCCD9A EC7DD02D
    40H 34867077 30476DC0 3D044B19 39C556AE
    44H 278206AB 23431B1C 2E003DC5 2AC12072
    48H 128E9DCF 164F8078 1B0CA6A1 1FCDBB16
    4CH 018AEB13 054BF6A4 0808D07D 0CC9CDCA
    50H 7897AB07 7C56B6B0 71159069 75D48DDE
    54H 6B93DDDB 6F52C06C 6211E6B5 66D0FB02
    58H 5E9F46BF 5A5E5B08 571D7DD1 53DC6066
    5CH 4D9B3063 495A2DD4 44190B0D 40D816BA
    60H ACA5C697 A864DB20 A527FDF9 A1E6E04E
    64H BFA1B04B BB60ADFC B6238B25 B2E29692
    68H 8AAD2B2F 8E6C3698 832F1041 87EE0DF6
    6CH 99A95DF3 9D684044 902B669D 94EA7B2A
    70H E0B41DE7 E4750050 E9362689 EDF73B3E
    74H F3B06B3B F771768C FA325055 FEF34DE2
    78H C6BCF05F C27DEDE8 CF3ECB31 CBFFD686
    7CH D5B88683 D1799B34 DC3ABDED D8FBA05A
    80H 690CE0EE 6DCDFD59 608EDB80 644FC637
    84H 7A089632 7EC98B85 738AAD5C 774BB0EB
    88H 4F040D56 4BC510E1 46863638 42472B8F
    8CH 5C007B8A 58C1663D 558240E4 51435D53
    90H 251D3B9E 21DC2629 2C9F00F0 285E1D47
    94H 36194D42 32D850F5 3F9B762C 3B5A6B9B
    98H 0315D626 07D4CB91 0A97ED48 0E56F0FF
    9CH 1011A0FA 14D0BD4D 19939B94 1D528623
    A0H F12F560E F5EE4BB9 F8AD6D60 FC6C70D7
    A4H E22B20D2 E6EA3D65 EBA91BBC EF68060B
    A8H D727BBB6 D3E6A601 DEA580D8 DA649D6F
    ACH C423CD6A C0E2D0DD CDA1F604 C960EBB3
    B0H BD3E8D7E B9FF90C9 B4BCB610 B07DABA7
    B4H AE3AFBA2 AAFBE615 A7B8C0CC A379DD7B
    B8H 9B3660C6 9FF77D71 92B45BA8 9675461F
    BCH 8832161A 8CF30BAD 81B02D74 857130C3
    C0H 5D8A9099 594B8D2E 5408ABF7 50C9B640
    C4H 4E8EE645 4A4FFBF2 470CDD2B 43CDC09C
    C8H 7B827D21 7F436096 7200464F 76C15BF8
    CCH 68860BFD 6C47164A 61043093 65C52D24
    D0H 119B4BE9 155A565E 18197087 1CD86D30
    D4H 029F3D35 065E2082 0B1D065B 0FDC1BEC
    D8H 3793A651 3352BBE6 3E119D3F 3AD08088
    DCH 2497D08D 2056CD3A 2D15EBE3 29D4F654
    E0H C5A92679 C1683BCE CC2B1D17 C8EA00A0
    E4H D6AD50A5 D26C4D12 DF2F6BCB DBEE767C
    E8H E3A1CBC1 E760D676 EA23F0AF EEE2ED18
    ECH F0A5BD1D F464A0AA F9278673 FDE69BC4
    F0H 89B8FD09 8D79E0BE 803AC667 84FBDBD0
    F4H 9ABC8BD5 9E7D9662 933EB0BB 97FFAD0C
    F8H AFB010B1 AB710D06 A6322BDF A2F33668
    FCH BCB4666D B8757BDA B5365D03 B1F740B4
    “驱动表法”的程序:// 注意:因生成项POLY最高位一定为“1”,故略去最高位的"1",
    unsigned short cnCRC_16 = 0x8005; // CRC-16 = X16 + X15 + X2 + X0  
    unsigned short cnCRC_CCITT = 0x1021; // CRC-CCITT = X16 + X12 + X5 + X0,据说这个 16 位 CRC 多项式比上一个要好  
    unsigned long cnCRC_32 = 0x04C11DB7; //采用正规的CRC32的POLY
    unsigned long Table_CRC16[256]; // CRC16 表  
    unsigned long Table_CRC32[256]; // CRC32 表  
    // 构造 16 位 CRC 表 "直接查询表"
    unsigned short i16, j16;  
    unsigned short nData16;  
    unsigned short nAccum16;  
    for ( i16 = 0; i16 < 256; i16++ )  
    {  
    nData16 = ( unsigned short )( i16 << 8 );  
    nAccum16 = 0;  
    for ( j16 = 0; j16 < 8; j16++ )  
    {  
    if ( ( nData16 ^ nAccum16 ) & 0x8000 )  
    nAccum16 = ( nAccum16 << 1 ) ^ cnCRC_16; //也可以用cnCRC_CCITT
    else  
    nAccum16 <<= 1;  
    nData16 <<= 1;  
    }  
    Table_CRC16[i16] = ( unsigned long )nAccum16;  
    }  // 构造 32 位 CRC 表 "直接查询表"
    unsigned long i32, j32;  
    unsigned long nData32;  
    unsigned long nAccum32;  
    for ( i32 = 0; i32 < 256; i32++ )  
    {  
    nData32 = ( unsigned long )( i32 << 24 );  
    nAccum32 = 0;  
    for ( j32 = 0; j32 < 8; j32++ )  
    {  
    if ( ( nData32 ^ nAccum32 ) & 0x80000000 )  
    nAccum32 = ( nAccum32 << 1 ) ^ cnCRC_32;  
    else  
    nAccum32 <<= 1;  
    nData32 <<= 1;  
    }  
    Table_CRC32[i32] = nAccum32;  
    }  
    unsigned char aData[512]={0x31,0x32,0x33,0x34}; //待测数据,为字串"1234"
    unsigned long aSize;
    unsigned long i;  
    unsigned char *point;// 计算 16 位 CRC 值,CRC-16 或 CRC-CCITT  
    //Table-Driven驱动表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据需扩展0
    unsigned short CRC16_1;  
    aSize=4; //数据长度字节(不包含扩展0)
    CRC16_1 = 0; //寄存器归0
    point=aData;
    while (aSize--)   
    CRC16_1 = ((CRC16_1 << 8) | *point++) ^ Table_CRC16[(CRC16_1 >> 8) & 0xFF];   
    for ( i = 0; i < 2; i++ )   
    CRC16_1 = ((CRC16_1 << 8) ) ^ Table_CRC16[(CRC16_1 >> 8) & 0xFF]; //加入2字节的扩展0
    //这时, CRC16_1中的值就是CRC// 计算 32 位 CRC-32 值  
    //Table-Driven驱动表法,需要用到“直接查询表”(不能用“正规查询表”);待测数据需扩展0
    unsigned long CRC32_1;  
    aSize=4; //数据长度字节(不包含扩展0)
    CRC32_1=0x0; //寄存器归0
      

  10.   

    en shi pian hao wen zhang ,xie de hen xiang xi 
      

  11.   

    呵呵,我以前做串口通信的时候,也用过CRC校验,但是很简单,只是在网上找了一个表,查询一下,楼主深入地很仔细了。
      

  12.   

    去年写的通讯协议用的modbus协议,里面用到过CRC32校验. 做通讯的人必须得掌握的校验方法
      

  13.   

    LZ你也写下CRC的中文写下吧,呵呵。我开始也没看懂是什么玩意呢。