C语言链表实战总结
来源:互联网 发布:ai人工智能 开源 编辑:程序博客网 时间:2024/04/27 13:25
近期项目里遇到瓶颈,我最后选择的方案是用链表解决又重新拾起了多年没用的链表。搞了一个星期,总算是把功能都实现了,并考虑了各种突发状况,基本上不会有程序挂掉的结果。不过过程中还是遇到了很多问题,这些问题在不断的翻书回顾中被解决。
背景是这样的,要求在液晶显示屏上创建一个窗口,里面至少有200个EDIT控件,用户可以在EDIT控件上编辑一些特定的语句,然后点击”运行“按钮时,保存现有的EDIT的内容并顺序执行各语句。当时抓耳挠腮,想卧槽这怎么搞,受做软件工程师学长的启发,我觉得用链表来操作应该没问题,编辑了几行EDIT就保存对应个数结点的链表,既节省空间而且方便顺序执行,执行到下一节点为NULL的时候结束就完了。而且链表结构的插入、删除时间复杂度都为O(1),很是方便。
首先是定义链表结构:
typedef struct _INSTRUCTOR {uint8_t index; //该节点位于链表中的索引uint8_t _flag; //该条程序的指令类别char *EditContent; //该条指令的文本内容struct _INSTRUCTOR* next; //指向下一条指令的指针}_Instructor,*_Listptr;_Listptr 就是_Instructor结构的指针类型了,就等于_Instructor *。注意咯,我这里的结构体没有用__attribute__定义它的属性,也就是说它是默认对齐的,一共占16bytes即0x10。为了省去考虑插入结点、删除结点时关于第一个结点的特殊情况问题,我创建了一个带有表头结点的链表:
_Listptr Ins_List_Head;//程序链表的头指针int Create_List(void){Ins_List_Head = (_Listptr)malloc(sizeof(_Instructor));if( !Ins_List_Head)return -1;Ins_List_Head -> index = 0;Ins_List_Head->next = (void*)0;return 0;}根据调试发现,每次链表申请到的头结点位置都在0X20004320.每增加一个结点,地址增加0x10,咦?大家发现问题没有,地址连续了!当然,这是我们希望看到的现象,不连续的话会造成内存碎片,还要进行碎片回收工作。所以我觉得可能是编译器优化的结果(如果有大神知道原因的话请指正!)
下面就要每编辑完一个EDIT,就要插入一个结点了,那么是前插还是后插呢?当然是后插了,不然让我怎么顺序执行啊。
int Add_Node(int index, enum _FLAG flag, char *content){int i = 0;_Listptr q = (_Listptr)malloc(sizeof(_Instructor));_Listptr p = Ins_List_Head;if(index <= 0 || !q)return -1;q -> index = index;q -> EditContent = content;q -> _flag = flag;while(p && i < index-1){p = p ->next ;//此时p即为下标为index的结点的前驱节点i ++;}if(!p)return -1;else{q -> next = p->next ;//使前驱的后继成为新增结点的后继p->next = q ; //使新增结点的称为前驱的后继}return 0;}这里要提一提 while循环里的循环次数为 index-1,这个-1很关键啊。道理当然很简单,插入的Node要把自己的头和尾都连接在链表上啊。我把新来的Node下标定为index,所以要在index-1处的结点就开始操作:将index-1的后继(其实就是NULL)成为新Node的后继,然后把新Node成为Index-1 的后继。一开始我粗心大意,直接 i<index ,而且没有检查每个节点的指针是否不为空(程序不健壮!),所以添加的第一个结点时程序就挂掉了,还傻了吧唧的说为什么,现在想想真实可笑!
经过修改后添加结点总算没问题了,然后就是删除结点的操作,我直接盗用书上的源码,很开心他可以直接用:
int Delete_Node(int index){int i;_Listptr p = Ins_List_Head;_Listptr q;if(index <= 0)return -1;for(i = 0; i< index - 1;i++)p = p -> next;//此时p为index结点的前驱结点if(!p){return -1;}else{q = p->next ;//q结点即为Index结点p -> next = q -> next;//使index结点的后继成为p的后继free(q); //释放index结点的空间return 0;}}道理更简单,就是把一个Node从链表上拆下来的过程:只要把他的前驱Node的next指针指向它的后继Node就ok了。
别以为做完这几步就可以做关于顺序执行方面的工作了,还要考虑有的用户编到一半,突然选择放弃不编了,点击"返回"按钮回到上一窗口时,难道我还保留着这些链表结点?当然不行啦,这样的话如果用户真正再开始编辑时,然后执行程序,发现有几步操作是上次编辑的没用的命令,这不是瞎搞吗!所以还要做的一个工作就是:点击 “返回”按钮就清空所有结点,当然头结点要保留啊。
void Clear_List(void){_Listptr p = Ins_List_Head ;_Listptr q ;int i = GetListLength() ;while(i--){q = p -> next;p -> next = q -> next;free(q);}}你能想象这段短短的小程序我调了整整一下午。所以人家说,一个好的程序员不在于他一天能写多少行代码,而是写多少行高效的代码,当然我不是说我是一个优秀的程序员啊。清空链表的思想跟删除结点的思想大致一样,只不过是一直操作头指针的next。一开始我的循环体没用while,而是用的for(i =0 ;i<GetListLength();i++) ,这样执行有个问题,我每次点击"返回"按钮后,只清空了一半的Node。我静静的想了想,这样写的话每次i<当前的结点长度,每删除一个结点长度都减一啊,瞎搞啊!!!真想抽自己大耳光。
谨以此文几年这一周蛋疼的链表生活。。。。。。
0 0
- C语言链表实战总结
- SQL 语言 实战总结
- c语言创建链表总结
- 《Linux C编程实战》中一些基础C语言知识 && 一些笔试面试题总结
- 【总结】C语言总结!!!
- [C语言]C总结
- C语言实战(一)
- C语言实战(二)
- C语言实战(三)
- C语言外挂实战【转】
- C语言实战练习:函数
- C语言 线性表描述 (实战片)
- C语言变量总结
- C语言要点总结
- C语言总结
- C语言输入输出总结
- C语言static总结
- C语言优先级总结
- 非上架AppStore的ipa分发方法
- SDWebImage 原理及使用
- 递归遍历json
- Swift网络编程
- 关于日期、数字的格式转换
- C语言链表实战总结
- HDU 4035 Maze [概率DP]
- Cannot send events to objects owned by a different thread
- 用swift 加载html文件
- new 一个私有内部类
- 修改NavigationView中的Item的Icon大小
- splay的入门
- Android的图片缓存 三级缓存
- Exception in thread "main" org.hibernate.HibernateException: No CurrentSessionContext configured!