《嵌入式数据结构笔记(三):数据结构基础与双向链表》
1.Makefile代码管理精要
基本语法规则
目标-依赖关系:
目标文件: 依赖文件 编译命令
自定义变量与指令
功能 | 语法 | 用途 |
---|---|---|
变量定义 | OBJ = a.out | 简化重复路径 |
变量引用 | $(OBJ) | 替代目标文件名 |
头文件指定 | -I include_path | 自定义头文件路径 |
库文件指定 | -L lib_path | 自定义库文件路径 |
清理操作 | make clean | 删除目标文件 |
时间戳机制
- 增量编译:仅重编译修改过的文件
- 效率提升:避免全量编译
示例:
2.GCC编译四步流程
阶段 | 指令 | 输入 | 输出 | 核心任务 |
---|---|---|---|---|
预处理 | gcc -E main.c -o main.i | .c | .i | 展开宏/头文件 |
编译 | gcc -S main.i -o main.s | .i | .s | 生成汇编代码 |
汇编 | gcc -c main.s -o main.o | .s | .o | 生成机器码 |
链接 | gcc main.o -o app | .o | 可执行文件 | 合并库与目标文件 |
3.双向链表核心操作
操作全流程
- 创建链表:初始化头尾指针
- 插入数据:头插法(高效)、尾插法(保序)
- 删除数据:头删、尾删、指定节点删除
- 数据操作:查找、修改、遍历
- 销毁链表:递归释放节点内存
//头文件#ifndef DOULINK_H #define DOULINK_H//创建data数据 typedef struct stu {int id;char name[32];int score; }Data_type_t;//创建结点对象 typedef struct dounode {Data_type_t data;struct dounode *ppre;struct dounode *pnext; }DNode_t;//创建双向链表对象 typedef struct doulink {DNode_t *phead;int clen; }DLink_t;//函数声明 extern DLink_t *create_doulink(); extern int insert_doulink_head(DLink_t *pdlink, Data_type_t data); extern int is_empty_doulink(DLink_t *pdlink); extern void DLink_for_each(DLink_t *pdlink, int dir); extern void insert_doulink_tail(DLink_t *pdlink, Data_type_t data); extern int delete_doulink_head(DLink_t *pdlink); extern int delete_doulink_tail(DLink_t *pdlink); extern void destory(DLink_t *pdlink); extern DNode_t *pnode(DLink_t *pdlink, char *name); extern DNode_t *modify(DLink_t *pdlink, char *name, int score); extern void delete_assign(DLink_t *pdlink, char *name);#endif
#include "doulink.h" #include <stdio.h> #include <stdlib.h> #include <string.h>//创建双向链表 DLink_t *create_doulink() {DLink_t *pdlink = malloc(sizeof(DLink_t));if(NULL == pdlink){printf("malloc error\n");}pdlink -> phead = NULL;pdlink -> clen = 0;return pdlink; }//判断链表是否为空 int is_empty_doulink(DLink_t *pdlink) {return NULL == pdlink -> phead; }//头插 int insert_doulink_head(DLink_t *pdlink, Data_type_t data) {DNode_t *pnode = malloc(sizeof(DNode_t));if(NULL == pnode){printf("malloc errror\n");}pnode -> data = data;pnode -> ppre = NULL;pnode -> pnext = NULL;if(is_empty_doulink(pdlink)){pdlink -> phead = pnode;}else{pnode -> pnext = pdlink -> phead;pdlink -> phead -> ppre = pnode;pdlink -> phead = pnode;}pdlink -> clen++;return 0; }//遍历 void DLink_for_each(DLink_t *pdlink, int dir) {if(is_empty_doulink(pdlink)){return;}DNode_t *ptmp = pdlink -> phead;if(1 == dir){while(ptmp){printf("%d,%s,%d\n", ptmp -> data.id, ptmp -> data.name, ptmp -> data.score);ptmp = ptmp -> pnext;}}else{while(ptmp->pnext){ptmp = ptmp -> pnext;}while(ptmp){printf("%d,%s,%d\n", ptmp -> data.id, ptmp -> data.name, ptmp -> data.score);ptmp = ptmp -> ppre;}} }//尾插 void insert_doulink_tail(DLink_t *pdlink, Data_type_t data) {if(is_empty_doulink(pdlink)){insert_doulink_head(pdlink, data);return;}else{DNode_t *pnode = malloc(sizeof(DNode_t));if(NULL == pnode){printf("malloc errror\n");}pnode -> data = data;pnode -> ppre = NULL;pnode -> pnext = NULL;DNode_t *ptmp = pdlink -> phead;while(ptmp -> pnext){ptmp = ptmp -> pnext; }ptmp -> pnext = pnode;pnode -> pnext = NULL;pnode -> ppre = ptmp;pdlink -> clen++;} }//头删 int delete_doulink_head(DLink_t *pdlink) {if(is_empty_doulink(pdlink)){printf("error\n");return -1;}DNode_t *pdel = pdlink->phead;pdlink->phead = pdel->pnext;if(pdlink->phead != NULL){pdlink->phead->ppre = NULL;}free(pdel);pdlink->clen--;return 0; }//尾删 int delete_doulink_tail(DLink_t *pdlink) {if(is_empty_doulink(pdlink)){printf("error\n");return -1;}DNode_t *ptmp = pdlink->phead;if (1 == pdlink->clen){free(ptmp);pdlink->phead = NULL;}else{while (ptmp->pnext != NULL){ptmp = ptmp->pnext;}ptmp->ppre->pnext = NULL;free(ptmp);pdlink->clen--;}return 0; }//摧毁链表 void destory(DLink_t *pdlink) {DNode_t *ptmp = pdlink -> phead;while(ptmp){pdlink->phead = ptmp->pnext;free(ptmp);ptmp = pdlink->phead;}free(pdlink); }//根据名字找结点 DNode_t *pnode(DLink_t *pdlink, char *name) {DNode_t *ptmp = pdlink -> phead;while(NULL != ptmp && strcmp(ptmp ->data.name , name) != 0){ptmp = ptmp -> pnext;}return ptmp; }//根据名字修改成绩 DNode_t *modify(DLink_t *pdlink, char *name, int score) {DNode_t *ptmp = pnode(pdlink, name);ptmp -> data.score = score;return ptmp; }//根据名字删除结点 void delete_assign(DLink_t *pdlink, char *name) {DNode_t *ptmp = pnode(pdlink, name);ptmp -> pnext -> ppre = ptmp -> ppre -> pnext;free(ptmp);pdlink -> clen--; }
//主函数#include <stdio.h> #include "doulink.h"int main(void) { //创建双向链表Data_type_t stus[5] = {{1, "zhangsan", 99},{2, "lisi", 100}, {3, "wangwu", 90}, {4, "maliu", 56}, {5, "tianqi", 66}};DLink_t *pdlink = create_doulink();if (NULL == pdlink){return -1;} //头插insert_doulink_head(pdlink, stus[0]);insert_doulink_head(pdlink, stus[1]);insert_doulink_head(pdlink, stus[2]);insert_doulink_head(pdlink, stus[3]);insert_doulink_head(pdlink, stus[4]);//遍历DLink_for_each(pdlink, 1);printf("\n");DLink_for_each(pdlink, 9); //尾插insert_doulink_tail(pdlink, stus[3]);DLink_for_each(pdlink, 1);printf("\n");DLink_for_each(pdlink, 9);//头删 DLink_for_each(pdlink,1);printf("\n");delete_doulink_head(pdlink);DLink_for_each(pdlink,1);//尾删DLink_for_each(pdlink,1);printf("\n");delete_doulink_tail(pdlink);DLink_for_each(pdlink,1);//摧毁链表destory(pdlink);//根据名字找结点DNode_t *ret = pnode(pdlink, "lisi");printf("%s\n", ret -> data.name);printf("%p\n", ret);//根据名字改成绩DNode_t *ret = modify(pdlink, "maliu", 0);printf("%d,%s,%d\n", ret -> data.id, ret -> data.name, ret -> data.score); //根据名字删结点 delete_assign(pdlink,"lisi");return 0; }
//makefileOBJ=a.out SRC=main.c SRC+=doulink.c INC=./ CC=gcc$(OBJ):$(SRC)$(CC) $^ -o $@ -I$(INC)clean:rm $(OBJ)
数据结构特点
特性 | 优势 | 局限 |
---|---|---|
双向指针 | 支持正反向遍历(O(1)访问相邻节点) | 额外8字节内存开销 |
动态内存 | 按需分配无限制 | 内存碎片风险 |
非连续存储 | 避免数据搬移 | CPU缓存命中率降低 |
4.关键技巧总结
Makefile高效实践
- 使用
%.o: %.c
通配符简化多文件编译 - 通过
-I
/-L
分离路径 clean
规则需包含rm -f *.o app
5.总结
Makefile核心价值
- 自动化编译与依赖管理
- 时间戳实现增量编译
GCC四层转换本质
源码 → 汇编 → 机器码 → 可执行文件
双向链表优势
- O(1)头尾操作
- 双向遍历灵活性
双向链表的结构