循环冗余校验(CRC)——C语言版

CRC目前是在数据通信和计算机网络中应用最广泛的一种校验编码方式。它的原理十分简单但非常使用,具体内容可自行百度,下面主要讲一下我的代码的算法:
先来一组名字解释:
Gx:生成码,这个是可以人为设定的,它就是CRC里面所谓的生成多项式对应的系数。
Kx:信息码,就是指要发送的信息,是一组1、0组合的字符串(当然可以看作是整数,或者浮点数等,在我的程序里是把它看作字符串的,长度可以自定)。
Tx:指真正发送出去的码字
Rx:指冗余码。
再来回顾一下它们之间的关系(举例说明),如果:
Kx=110011,Gx=11001;根据CRC运算规则可以算出Rx=1001,所以Tx=1100111001。
下面分段解释一下我的代码:

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

//#define CRC_CCITT 69665
//#define CRC_16 98309
//#define CRC_12 6159
#define DEC 2 //用于控制输出数据的进制表示,默认输出二进制

头文件没什么好说的,注释掉的那三行是国际标准下的Gx转化为十进制的值,最后一行是用来控制输出形式的,如果想把结果表示为十六进制输出,将2改为16即可。

unsigned btoi(char pt[])  //此函数用来把二进制字符串转化为十进制整数
{
    int le=0,i;
    unsigned sum=0;
    le=strlen(pt);
    for(i=0;i<le;i++)
    {
        if(pt[i]=='1')
           sum=sum+(unsigned)(pow(2,le-1-i)); //注释(一)
    }
    return sum;
}
///////////////////////////分界线///////////////////////////////////

unsigned CRC_Calc(unsigned *Kx,unsigned Gx)  //此函数用于计算十进制的冗余码
{
    int bi=0;
    char bu[32];
    itoa(Gx,bu,2);
    bi=strlen(bu);
    *Kx=(*Kx)*(unsigned)pow(2,bi-1);//注释(二)
    return Gx-(*Kx%Gx);
}

在上面两个函数中:第一个是进制转换函数,形参为字符串表示的二进制数据,函数返回十进制数据;后一个函数,是用于计算冗余码的,计算结果用十进制数据表示(两个函数都只能操作无符号整数),该函数形参为信息码的地址(因为要修改其值,而且要返回冗余码值)和生成码。其中函数itoa()是C语言的一个库函数(对应的头文件是stdlib.h)意为:intergrate to ascii,就是把十进制的整数转化为其他进制的、ASCII表示的字符串,转化为几进制由最后一个形参决定。
注释(一):根据二进制转化为十进制的计算方法,这一步就是把某一位不为0的权值先算出来在加到总和里面;
注释(二):这一步就是K(x)*x^r,见下图:
CRC冗余码计算原理

下面看主函数:

int main()
{
    int f=1;  //f表示flag,用于决定是否继续测试
    unsigned g,k,s,r; //分别意为:Gx,Kx,Tx,Rx
    char se[32],rx[32],gi[32],ki[32];
    while(f) 
    {
        system("cls"); //清屏函数
        fflush(stdin); //清空缓冲区

/* ************ 十进制输入*********************/

//        printf("请输入十进制信息码:\n");
//        scanf("%d",&k);
//        itoa(k,ki,DEC);
//        printf("请输入十进制生成码:\n");
//        scanf("%d",&g);
//        itoa(g,gi,DEC);
/* ****************二进制输入*********************** */

        printf("请输入二进制信息码:\n");
        gets(ki);
        k=btoi(ki);
        printf("请输入二进制生成码:\n");
        gets(gi);
        g=btoi(gi);
 // ------------------------------分界线-----------------------------------     //
        r=CRC_Calc(&k,g);
        s=r+k;
        itoa(r,rx,DEC);
        itoa(s,se,DEC);
        printf("生成码(Gx):%s\n",gi);
        printf("信息码(Kx):%s\n",ki);
        printf("发送码(Tx):%s\n",se);
        printf("冗余码(Rx):%s\n",rx);
        if(s%g==0)     ////////////////注释(一)/////////////////////
        {
            printf("\n\tAcceptable!\n");
        }
        else
        {
            printf("\n\tUnacceptable!\n");
        }
        printf("\n是否继续(1=是,0=否)\n");
        scanf("%d",&f);
    }
    return 0;
}

上面的代码中:输出方式有两种,默认是二进制输入,当然可以选择十进制输入(下面会放图展示二者的区别),注释(一)以后的代码是对结果的验证,如果验证成功会打印出“Acceptable!”,否者会打印出“Unacceptable”。
下面看一下两种不同输入的区别:
十进制输入

二进制输入

总结:本段代码里面的计算方式与某些教科书上的计算方式有所区别,当然本人只是用C语言模拟运算一下,如有疑问、意见或者建议,欢迎大家指出!

由于本篇循环校验实现的方法不是严格按照书本上的实现,所以后面我又补充了另外一篇完全按照书本上要求实现的算法,到这里查看。
发布了25 篇原创文章 · 获赞 9 · 访问量 6206

猜你喜欢

转载自blog.csdn.net/qq_42144047/article/details/88696171