手把手教你用C写游程编码

手把手教你用C写游程编码


(原创作品,作者Shawn, 转载请声明)

相信大家对游程编码的概念不会陌生。如果用C语言亲自实现一遍游程编码的话,会发现综合运用到了很多C语言的知识和要避免踩到坑!

游程编码:

给定一串数据,如:
0x11,0x22,0x11,0x11,0x11,0x34,0x34,0x22
编码后,得到:
0x11,0x01,0x22,0x01,0x11,0x03,0x34,0x02,0x22,0x01

废话少说,立马手把手教你用C写游程编码!这里用到函数分层的思想,即模块化处理:
输入的数据–>用函数处理–>返回输出数据
即数据的处理完全由函数内部解决。

敲黑板划重点!!!
C语言就是玩转内存空间
C语言就是玩转内存空间!!
C语言就是玩转内存空间!!!

因此要时刻谨记和扪心自问三个哲学问题:

1. 要操作的对象是什么?(大小,即数据类型)

2. 分配的内存空间在哪里?(内存的哪个段)

3. 处理后的内存空间要去哪里?(注意指针的坑)


下面展开来说。

要操作的对象是什么?

  • 根据对象的需要,分配相应的数据类型。数据类型可以分为两种:
    1. 内置数据类型:比如,int, float, double.
    2. 自定义数据类型:比如,结构体, 数组(本质是内置数据类型的组合)。结构体适用于不同内置数据类型的组合,例如应用在通信协议的设计;数组适用于也必须用于相同数据类型的组合。

先看原始数据

原始数据是1字节的序列,即大小为1字节的(char)、连续的内存空间。可见,输入数据可使用数组表示:

char raw_data[]={
  0x11,0x22,0x11,0x11,0x11,0x34,0x34,0x22};

如果你愿意更严谨一些,可以表示为

unsigned char raw_data[]={
  0x11,0x22,0x11,0x11,0x11,0x34,0x34,0x22};

以此来向别人表明这些数据是不考虑正负号的。
但其实两种写法都可以,原则上都ok(因为编译器才是老大,它说ok就妥妥的)。
此处为了方便,采用char raw_data[]的写法。


原始数据的内存空间应该分配在哪里呢?

这引出了新的问题,分配的内存在哪几个段(segment)?

内存分布图

具体如下:

地址值 备注1 备注2
高地址 stack 栈 动态内存分配
heap 堆 动态内存分配
bss 未初始化全局变量或静态(全局或局部)变量 可读可写* 静态内存分配
data 已初始化全局变量或静态(全局或局部)变量 可读可写* 静态内存分配
低地址 text 代码段+字符串常量 只读 静态内存分配

* 也存在只读变量,此处不展开说明。

此处,我们简单粗暴地把原始数据空间定义在main函数里,也就是说,是在栈空间,因为定义在main函数内部,且没有static修饰符,所以既不是全局变量、也不是静态变量。
详情可参考该链接


再看结果数据:

此处我要用一个名为runLenEncoding的函数(run-length encoding游程编码)来处理输入数据。
那么现在问题来了,在真正调用该函数,传入raw_data数据之前,该函数根本不知道需要为结果数据分配多少内存空间(因为这里假定所有数据处理都在该函数内部,所以不能在进入该函数前就处理数据,从而无法预先得知结果数据空间的大小!)。

好纠结,纠结不如敲几行代码:

#include<stdio.h>
void runLenEncoding(void);
int main()
{
    //原始数据空间,这里简单粗暴地放在栈空间即可
    char raw_data[]={
  0x11,0x22,0x11,0x11,0x11,0x34,0x34,0x22};
    //结果数据空间。应该是啥?

    //执行函数,进行游程编码,返回什么给结果数据空间?
    result_data=runLengEncoding();

    return 0;
}

结果数据的内存空间应该分配在哪里呢?

考虑到在runLenEncoding函数中,经过一定数据处理,才能得知结果空间的大小。也就是说,不可能是静态内存分配,函数还没处理,我怎么知道需要为结果数据分配多大空间?
剩下两个动态内存分配:
1. 。还是不靠谱:
- 函数一结束返回,则栈空间的数据就销毁,返回的地址就没有意义了——因为此时该地址的数据已经被系统回收了,不存在了。
- 更何况,类似静态内存分配那样,在编写代码时就需要确定空间的大小,因而同样无法满足函数需要动态增加内存的需求
2. 。妥妥的。堆具有两个优势:
- 堆空间的生命周期由程序员说了算,由程序员编写代码创造和毁灭。也就是说,通过malloc()函数创造的堆空间,在用了free()函数后,该空间才会被销毁。因此,不存在栈空间的缺点:在函数返回后,栈空间则销毁(数据/资源被系统回收)。
- 堆空间可以不是在编写程序时就规定好的。也就是,可以根据程序实际需要,真正地实现按需分配空间大小

因此,在runLenEncoding函数内部创建堆空间,即结果数据空间,返回该数据空间的首地址。

处理后的内存要去哪里?

main函数用一个result_data指针接受该地址。


妥,继续敲几行。

#include<stdio.h>
char* runLenEncoding(const char*);
int main()
{
    //原始数据空间,这里简单粗暴地放在栈空间即可
    char raw_data[]={
  0x11,0x22,0x11,0x11,0x11,0x34,0x34,0x22};
    //结果数据空间(实质是指向结果数据空间的指针)。初始化指向NULL
    char *result_data=NULL;
    //调用函数,进行游程编码,返回堆空间(结果数据空间)的首地址给main函数中的结果指针?
    result_data=runLenEncoding(raw_data);
    //返回指针后,对指针进行检查
    if(result_data==NULL){
        printf("Get result_data failed!\n");
        return -1;
    }
    return 0;
}

打印,检查输入和输出

仔细想想,输出的数据我要打印出来检查一下对不对呀。要操作的对象是数组,脑海里立马是for循环。既然要打印输出数据,那把输入数据也打印吧,正好对比着看,那就写一个check_data函数,两个都可以调用(模块化设计哦!)。

用到了for循环,那条件中的结束标志当然是数组中的数据个数,因此要分别定义两个变量raw_dataresult_data

raw_data可以简单粗暴地求出来:

raw_num=sizeof(raw_data)/sizeof(raw_data[0
  • 8
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值