当前位置: 首页 > news >正文

《嵌入式数据结构笔记(四):栈结构与队结构链表》

1.内核链表设计精要

        内核链表:双向循环链表
不再将数据存储在链表节点中,而是将结点嵌入到存储的数据中。

  • 传统链表缺陷
    struct Node {int data;            // 数据与节点耦合struct Node *next;
    };
    
  • 内核链表方案
    struct list_head {     // 纯链表节点struct list_head *prev, *next;
    };
    struct task_struct {   // 业务数据结构int pid;struct list_head tasks; // 节点嵌入业务数据
    };
    
关键宏操作
功能实现原理
offset_of计算成员在结构体中的偏移量(size_t)&((type*)0)->member
container_of通过成员地址获取结构体首地址(type*)((char*)ptr - offset)

2.栈与队列实现机制

栈结构(FILO)

栈:只允许从一端进行数据的插入和删除的线性存储结构,称之为栈结构

特点:先进后出: FILO

顺序栈(数组),满增栈、满减栈、空增栈、空减栈

满栈、空栈:栈顶所在位置是否存在数据

增栈、减栈:按照栈的生长方向区分

链式栈

1.创建链式栈
2. 入栈(头插)
3. 出栈(头删)
4. 判断是否为空栈
5. 获取栈顶元素
6. 销毁栈

//.h#ifndef _SATCK_H_
#define _STACK_H_typedef int Data_type_t;typedef struct stnode
{Data_type_t data;struct stnode *pnext;
}STNode_t;typedef struct stack 
{STNode_t *ptop;int clen;
}Stack_t;extern Stack_t *create_stack();
extern int is_empty_stack(Stack_t *pstack);
extern void stack_for_each(Stack_t *pstack);
extern int push_stack(Stack_t *pstack, Data_type_t mydata);
extern int pop_stack(Stack_t *pstack, Data_type_t *pdata);
extern int get_top_stack(Stack_t *pstack, Data_type_t *pdata);
extern void destroy_stack(Stack_t **pstack);
#endif
//fun.c#include "stack.h"
#include <stdio.h>
#include <stdlib.h>Stack_t *create_stack()
{Stack_t *pstack = malloc(sizeof(Stack_t));if(NULL == pstack){printf("malloc error\n");}pstack -> ptop = NULL;pstack -> clen = 0;return pstack;
}int is_empty_stack(Stack_t *pstack)
{return NULL == pstack -> ptop;
}void stack_for_each(Stack_t *pstack)
{if(is_empty_stack(pstack)){return;}STNode_t *ptmp = pstack -> ptop;while(NULL != ptmp){printf("%d\n", ptmp -> data);ptmp = ptmp -> pnext;}
}//入栈(toucha)
int push_stack(Stack_t *pstack, Data_type_t mydata)
{STNode_t *pstnode = malloc(sizeof(STNode_t));if(NULL == pstnode){printf("malloc errror\n");}pstnode -> data = mydata;pstnode -> pnext = NULL;if(is_empty_stack(pstack)){pstack -> ptop = pstnode;}else{pstnode -> pnext = pstack -> ptop;pstack -> ptop = pstnode;}pstack -> clen--;return 0;
}//出栈(toushan)
int pop_stack(Stack_t *pstack, Data_type_t *pdata)
{if(is_empty_stack(pstack)){return -1;}else{STNode_t *ptmp = pstack -> ptop;pstack -> ptop = ptmp -> pnext;if(pdata != NULL){*pdata = ptmp -> data;}free(ptmp);pstack -> clen--;return 0;}
}
//取栈顶元素
int get_top_stack(Stack_t *pstack, Data_type_t *pdata)
{if(is_empty_stack(pstack)){return -1;}else{STNode_t *ptmp = pstack -> ptop;*pdata = ptmp -> data;return *pdata;}}
//销毁栈链表
void destroy_stack(Stack_t **ppstack)
{while (!is_empty_stack(*ppstack)){pop_stack(*ppstack, NULL);}free(*ppstack);*ppstack = NULL;
}
//main.c#include <stdio.h>
#include "stack.h"int main(void)
{Stack_t *pstack = create_stack();if(NULL == pstack){return -1;}push_stack(pstack, 6);push_stack(pstack, 5);push_stack(pstack, 4);push_stack(pstack, 3);push_stack(pstack, 2);push_stack(pstack, 1);stack_for_each(pstack);int pdata;printf("%d\n", get_top_stack(pstack, &pdata));destroy_stack(&pstack);return 0;
}
//makefileOBJ=a.out
SRC=main.c
SRC+=stack.c
INC=./
CC=gcc$(OBJ):$(SRC)$(CC) $^ -o $@ -I$(INC)clean:rm $(OBJ)

队列结构(FIFO)

队列:允许从一端进行数据的插入,另一端进行数据删除的线性存储结构,称为队列结构
插入操作,叫入队操作,插入的这端称为队列的队尾;
删除操作,叫出队操作,删除的这端称为队列的队头。

顺序队列(数组):[head, tail)

        假溢出问题:循环队列为了区分队空和队满,将来少存储一个数据。
循环队列如何判空?队头和队尾处于同一位置,此时认为队列为空
循环队列如何判满?当队尾+1跟上队头时,任务认为队列为满

1. 创建循环队列
2. 入队操作
3. 出队操作
4. 判空
5. 获取队头元素
6. 销毁队列
7. 遍历队列

链式队列

1. 创建链式队列
2. 入队操作(尾插)
3. 出队操作(头删)
4. 判空
5. 获取队头元素
6. 销毁队列
7. 遍历队列


//.h#ifndef __LQUEUE_H__
#define __LQUEUE_H__typedef int Data_type_t;typedef struct lqnode
{Data_type_t data;struct lqnode *pnext;
}LQNode_t;typedef struct lqueue
{LQNode_t *phead;LQNode_t *ptail;int clen;
}LQueue_t;extern LQueue_t *creat_lqueue();
extern int is_empty_queue(LQueue_t *pqueue);
extern void queue_for_each(LQueue_t *pqueue);
extern int push_queue(LQueue_t *pqueue, Data_type_t mydata);
extern int pop_queue(LQueue_t *pqueue, Data_type_t *pdata);
extern int get_top_queue(LQueue_t *pqueue, Data_type_t *pdata);
extern void destroy_queue(LQueue_t **ppqueue);#endif
//fun.c#include <stdio.h>
#include <stdlib.h>
#include "lqueue.h"LQueue_t *creat_lqueue()
{LQueue_t *pqueue = malloc(sizeof(LQueue_t));if(NULL == pqueue){printf("malloc error\n");}pqueue -> phead = NULL;pqueue -> ptail = NULL;pqueue -> clen = 0;return pqueue;
}int is_empty_queue(LQueue_t *pqueue)
{return NULL == pqueue -> phead && NULL == pqueue -> ptail;
}void queue_for_each(LQueue_t *pqueue)
{if(is_empty_queue(pqueue)){return;}LQNode_t *ptmp = pqueue -> phead;while(NULL != ptmp){printf("%d\n", ptmp -> data);ptmp = ptmp -> pnext;}
}int push_queue(LQueue_t *pqueue, Data_type_t mydata)
{LQNode_t *plqnode = malloc(sizeof(LQNode_t));if(NULL == pqueue){printf("malloc errror\n");}plqnode -> data = mydata;plqnode -> pnext = NULL;if(is_empty_queue(pqueue)){pqueue -> phead = plqnode;pqueue -> ptail = plqnode;}else{LQNode_t *ptmp = pqueue -> phead;while(NULL != ptmp -> pnext){ptmp = ptmp -> pnext;}ptmp -> pnext = plqnode;pqueue -> ptail = plqnode;}pqueue -> clen++;return 0;
}int pop_queue(LQueue_t *pqueue, Data_type_t *pdata)
{if(is_empty_queue(pqueue)){return -1;}else{LQNode_t *ptmp = pqueue -> phead;pqueue -> phead = ptmp -> pnext;if(pdata != NULL){*pdata = ptmp -> data;}if(pqueue -> clen == 1){pqueue -> ptail = NULL;}free(ptmp);pqueue -> clen--;return 0;}
}int get_top_queue(LQueue_t *pqueue, Data_type_t *pdata)
{if(is_empty_queue(pqueue)){return -1;}else{LQNode_t *ptmp = pqueue -> phead;*pdata = ptmp -> data;return *pdata;}
}void destroy_queue(LQueue_t **ppqueue)
{while(!is_empty_queue(*ppqueue)){pop_queue(*ppqueue, NULL);}free(*ppqueue);*ppqueue = NULL;
}
//main.c
#include<stdio.h>
#include"lqueue.h"int main(void)
{LQueue_t *pqueue = creat_lqueue();if(NULL == pqueue){return -1;}push_queue(pqueue, 1);push_queue(pqueue, 2);push_queue(pqueue, 3);push_queue(pqueue, 4);push_queue(pqueue, 5);queue_for_each(pqueue);puts("");puts("");Data_type_t i;pop_queue(pqueue, &i);queue_for_each(pqueue);printf("delete number: %d\n", i);Data_type_t j;get_top_queue(pqueue, &j);printf("%d\n", j);destroy_queue(&pqueue);return 0;
}
//makefileOBJ=a.out
SRC=main.c
SRC+=lqueue.c
INC=./
CC=gcc$(OBJ):$(SRC)$(CC) $^ -o $@ -I$(INC)clean:rm $(OBJ)

3.二级指针核心原理

二级指针本质
  • 应用场景
    • 修改主调函数中的一级指针(传递指针的地址)
    • 接收指针数组作为参数(char* arr[10]char**
  • 操作模型
    void modify_ptr(int **pptr) {*pptr = malloc(sizeof(int)); // 修改外部指针指向
    }
    

指针数组解析
类型声明内存模型
字符指针数组char *a[10]连续存储10个char*地址
整型指针数组int *a[10]连续存储10个int*地址
  • 数组名本质:首元素地址(a&a[0],类型为char**

4.关键技巧总结

  • 二级指针安全操作

    • 解引用前校验空指针:if (!pptr) return;
    • 类型匹配检查:int**不能操作char**
  • 内核链表工程优势

    • 通用性:同一链表操作复用所有数据结构
    • 零拷贝:数据移动只需修改指针
    // 任务迁移示例
    list_move(&task1->tasks, &new_queue);
    

  • 循环队列设计规范

    • 容量定义为2^n(位运算优化模计算:index & (size-1)
    • 内存屏障保证多线程安全

5.总结

  • 二级指针本质

    • 指针的指针 → 跨函数修改指针的终极工具
    • 指针数组枢纽 → 字符串表/命令参数处理核心
  • 内核链表哲学

    • 解耦数据与结构(Linux内核基石)
    • container_of宏 → C语言"面向对象"实现
  • 栈与队列本质差异

    结构操作端应用场景
    单端操作(TOP)函数调用/表达式求值
    队列双端操作消息缓冲/任务调度
  • 嵌入式场景适配

    • 静态分配循环队列(无动态内存需求)
    • 链式栈实现深度递归(避免栈溢出)
http://www.lryc.cn/news/612432.html

相关文章:

  • Chrontel【7322BMF】CH7322B HDMI Consumer Electronics Control (CEC) devices
  • GaussDB 数据库架构师修炼(六)-3 集群工具管理-主备倒换
  • prometheus+Grafana 监控中间件项目
  • 202506 电子学会青少年等级考试机器人四级实际操作真题
  • 架构层防护在高并发场景下的实践
  • 机器学习-LinearRegression
  • 机器学习模型调优实战指南
  • 机器学习——SVM
  • 居家养老场景下摔倒识别准确率提升 29%:陌讯动态姿态建模算法实战解析
  • 第五十一章:AI模型服务的“百变面孔”:WebUI/CLI/脚本部署全解析
  • 从原理图到PCB的布局
  • LiveQing视频RTMP推流视频点播服务功能-云端录像支持按时间段下载录像时间段下载视频mp4
  • STM32的PWR
  • 引领GameFi 2.0新范式:D.Plan携手顶级财经媒体启动“龙珠创意秀”
  • ZYNQ实现FFT信号处理项目
  • Python科学计算:从基础到工程仿真的完整指南
  • 指挥中心自动化的演变
  • cygwin+php教程(swoole扩展+redis扩展)
  • Redis使用的常见问题及初步认识
  • JDK17新特性全解析
  • TFTP: Linux 系统安装 TFTP,文件系统启动后TFTP使用
  • win服务器系统10060问题解决
  • 【CSS】动态修改浏览器滚动条宽度
  • 楼宇自控系统对建筑碳中和目标的实现具重要价值
  • mysql死锁的常用解决办法
  • Java 里的Tree初认识
  • 【概念学习】什么是深度学习
  • 12、Docker Compose 安装 Redis
  • Effective C++ 条款25:考虑写出一个不抛异常的swap函数
  • 【前端开发】五. ES5和ES6对比