Linux嵌入式学习——数据结构——线性表的链式结构
线性表的链式存储
解决顺序存储的缺点,插入和删除,动态存储问题。
特点:
线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占用的位置上。
所以前面的顺序表只需要存储数据元素信息就可以了。在链式结构中还需要一个元素存储下一个元素的地址。
为了表示每个数据元素,ai与其直接后继数据元素ai+1之间的逻辑关系,对ai来说,除了存储其本身的信息外,还需要存一个指示器直接后续的信息。把存储元素信息的域叫数据域,把存储直接后继位置的域叫指针域。这两部分信息组成数据元素ai的存储映像,叫结点(Node);
单链表中,c语言的描述
typedef struct person {
char name[32];
char sex;
int age;
int score;
}DATATYPE;
typedef struct node {
DATATYPE data;
struct node *next,*prev; ->这里必须要加指针
}LinkNode;
typedef struct list {
LinkNode *head;
int tlen; 链表元素总个数 这里可以不要
int clen; 当前节点个数
(mutex 这部分有可能是并发的,有可能需要加上互斥锁)
}LinkList;
以下是一个链表(LinkList)的操作集,这些操作包括创建链表、在链表头部或尾部插入节点、显示链表内容、查找链表中的节点、删除链表中的节点、修改链表节点的数据和销毁链表。这里,我假设LinkList是一个指向链表头节点的指针,而LinkNode是链表节点的结构体。DATATYPE是一个占位符,表示链表节点存储的数据类型,可能是整型、字符型或其他自定义类型。以下是对每个函数的基本实现思路(不包含完整代码):
1. LinkList *CreateLinkList(int len);
这个函数用于创建一个具有指定长度(len)的空链表。如果len是0,则创建一个空链表(只有一个头节点,头节点的下一个指针指向NULL)。如果len大于0,则可能需要根据实际需求创建len个空节点(或带初始值的节点)的链表。但通常,len参数在这里可能不太适用,因为链表长度是动态变化的,更常见的做法是不带参数地创建一个空链表。

2. int InsertHeadLinkList(LinkList *list, DATATYPE data);
在链表的头部插入一个新节点,该节点包含指定的数据data。函数返回成功或失败的状态码。
若需要在当前表头结点插入一个新结点 , 只需要修改一个next指针(新结点的next指针) , 可以分两步完成
-
定义节点结构体:首先,需要定义一个节点结构体,该结构体包含数据域和指针域。数据域用于存储节点的数据,指针域用于存储指向下一个节点的指针。
-
初始化链表:创建一个头节点,该节点不存储有效数据,仅作为链表的入口点。头节点的指针域初始化为NULL,表示链表为空。
-
插入新节点:对于每次插入操作,首先创建一个新节点,并为其数据域赋值。然后,将新节点的指针域指向头节点的下一个节点(即原链表的第一个节点),最后修改头节点的指针域,使其指向新节点。
* 修改新结点的next指针 , 使其指向当前的表头结点

* 更改表头指针的值 , 使其指向新节点(之前的表头指针指向的是15 , 修改后指向为新数据的)
3. int ShowLinkList(LinkList *list);
遍历链表,并打印出每个节点的数据。这个函数没有返回值(或返回一个状态码表示成功或失败,但通常打印操作不需要)。

4. LinkNode *FindLinkList(LinkList *list, char *name);
在链表中查找具有指定名称(name)的节点。由于函数返回类型为LinkNode *,它返回找到的节点的指针,如果未找到则返回NULL。这里假设每个节点都有一个name属性,这与DATATYPE数据类型的定义可能不一致,需要根据实际数据结构调整。
其中可以定义一个函数指针,来找结构体中不同得东西来达到解耦合的情况



5. int DeleteLinkList(LinkList *list, char *name);
从链表中删除具有指定名称(name)的节点。函数返回成功或失败的状态码。
删除中间节点
(1)使tmp指向想要删除的节点

(2)将tmp的下一个的prev与tmp本身的上一个相等

(3)使tmp的上一个的nxt与tmp本身的next相等 得到指向tmp节点的下一个节点。

(4)但如果是最后的节点 ,只要使前一个的下一个置为空就行。但无法找到本身的下一个,会发生错误,所以需要加上一个判断。

6. int ReviseLinkList(LinkList *list, char *name, DATATYPE data);
使链表反转
定义三个指针,分别表示正在操作的节点(prve),以及该节点的上一个(tmp),该节点的下一个(next)
(1)将tmp=head;prve=NULL;next=tmp->next;
(2)反转tmp本身的prve和next指针
tmp-》next=prve; tmp->prev=next;
(3)将最初定义的大指针一个一个往后移,直到最后tmp为空,
prve = tmp;
tmp=next;
next =next->next;
(4)将head与最初定义的prev相等。


7. int DestroyLinkList(LinkList *list);
销毁链表,释放链表占用的所有内存空间。函数返回成功或失败的状态码。
使tmp指向第一个节点,一个一个往后释放,最后释放head;

8. int InsertTailLinkList(LinkList *list, DATATYPE data);
在链表的尾部插入一个新节点,该节点包含指定的数据data。函数返回成功或失败的状态码。
9. int ModifyDouLinkList(LinkList *list, DATATYPE data);
改变某个节点的数据。函数返回成功或失败的状态码

9. int InserPosDouLinList(LinkList *list, DATATYPE data);
(1)如果head里没有节点 clen=0(链表是空)或者想要插入的pos=0;直接调用头插
(2)head后有节点
将tmp移到想要插入位置的前一个节点;
while(pos-1)
{
tmp=tmp->next;
}


使newnode的prev指向tmp,newnode 的next指向原来tmp指向的位置;
newnode->prve=tmp;
newnode->next=tmp->next;
如果tmp后面没东西,直接使tmp的next等于newnode
(原本就定义了newnode的prev和next是NULL);
如果tmp后面 ,使原本ttmp的下一个的前向指针指向newnode 同时使 tmp 的后向指针指向newnode。
即:
if(tmp->next)
{
tmp->next->prev=newnode
}
tmp->next=newnode;

pos和clen相同就是尾插。
.h
#ifndef DOULINK_H
#define DOULINK_H
typedef struct{char name[32];char sex;int age;int score;
}DATATYPE;
typedef int (*PFUN)(DATATYPE*data,void* arg);
typedef struct node {DATATYPE data;struct node *next,*prev;
}DouLinkNode;typedef struct{DouLinkNode *head;int clen;
}DouLinkList;
typedef enum{DIR_FORWARD,DIR_BACKWARD}DIRECT;
DouLinkList* CreateDouLinkList();
int InsertHeadLinkList(DouLinkList *list, DATATYPE *data);
int ShowDouLinkList(DouLinkList *list,DIRECT direct);
int GetSizeDouLinkList(DouLinkList *list);
DouLinkNode *FindLinkList(DouLinkList *list, PFUN fun,void* arg);
int RevertDouLinkList(DouLinkList *list);
int DeleteLinkList(DouLinkList *list, PFUN fun,void* arg);
int IsEmptyDouLinkList(DouLinkList *list);
int ModifyDouLinkList(DouLinkList *list,PFUN fun,void* arg,DATATYPE *data);
int DestroyDouLinkList(DouLinkList **list);
int InserPosDouLinkList(DouLinkList *list,DATATYPE *data,int pos);
#endif // DOULINK_H
.c
#include "doulink.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>DouLinkList *CreateDouLinkList()
{//DouLinkList dl ;DouLinkList* dl = (DouLinkList*)malloc(sizeof(DouLinkList));if(NULL == dl){perror("CreateDouLinkList malloc");//exit(1);return NULL;}dl->head =NULL;dl->clen = 0 ;return dl;
}int InsertHeadLinkList(DouLinkList *list, DATATYPE *data)
{DouLinkNode*newnode = malloc(sizeof(DouLinkNode));if(NULL == newnode){perror("InsertHeadLinkList malloc");return 1;}memcpy(&newnode->data,data,sizeof(DATATYPE));newnode->next = NULL;newnode->prev= NULL;if(0==list->clen)//empty{list->head = newnode;}else{newnode->next = list->head;list->head->prev = newnode;list->head = newnode;}list->clen++;return 0;}int ShowDouLinkList(DouLinkList *list, DIRECT direct)
{int i = 0 ;DouLinkNode* tmp = list->head;if(direct==DIR_FORWARD){for(i=0;i<GetSizeDouLinkList(list);i++){printf("%s %c %d %d\n",tmp->data.name,tmp->data.sex,tmp->data.age,tmp->data.score);tmp=tmp->next;}}else{while(tmp->next){tmp=tmp->next;}for(i=0;i<GetSizeDouLinkList(list);i++){printf("%s %c %d %d\n",tmp->data.name,tmp->data.sex,tmp->data.age,tmp->data.score);tmp=tmp->prev;}}return 0;
}int GetSizeDouLinkList(DouLinkList *list)
{return list->clen;
}DouLinkNode *FindLinkList(DouLinkList *list, PFUN fun, void *arg)
{DouLinkNode* tmp = list->head;int size = GetSizeDouLinkList(list);int i = 0;for(i = 0 ;i<size;i++){//if(0==strcmp(tmp->data.name))if(fun(&tmp->data,arg)){return tmp;}tmp= tmp->next;}return NULL;
}int RevertDouLinkList(DouLinkList *list)
{int size = GetSizeDouLinkList(list);if(size<2){return 0;}DouLinkNode* prev= NULL;DouLinkNode* tmp = list->head;DouLinkNode*next= tmp->next;while(1){tmp->next = prev;tmp->prev = next;prev= tmp;tmp = next;if(NULL == tmp){break;}next =next->next;}list->head = prev;return 0;
}int DeleteLinkList(DouLinkList *list, PFUN fun, void *arg)
{if(NULL == list){fprintf(stderr,"DouLinkList is null");return 1;}if(IsEmptyDouLinkList(list)){fprintf(stderr,"DouLinkList is empty");return 1;}DouLinkNode* ret = FindLinkList(list,fun,arg);if(NULL==ret){fprintf(stderr,"DeleteLinkList error,cant find\n");return 1;}if(ret == list->head){list->head = ret->next;list->head->prev = NULL;}else{if(ret->next)ret->next->prev = ret->prev;ret->prev->next = ret->next;}free(ret);list->clen--;return 0;
}int IsEmptyDouLinkList(DouLinkList *list)
{return 0 == list->clen;
}int ModifyDouLinkList(DouLinkList *list, PFUN fun, void *arg, DATATYPE *data)
{DouLinkNode* ret = FindLinkList(list,fun,arg);if(NULL == ret){fprintf(stderr,"ModifyDouLinkList error,cant find\n");return 1;}memcpy(&ret->data,data,sizeof(DATATYPE));return 0;
}int DestroyDouLinkList(DouLinkList **list)
{DouLinkNode* tmp=(*list)->head;while(tmp){(*list)->head=(*list)->head->next;free(tmp);tmp = (*list)->head;}free(*list);(*list)= NULL;return 0;
}int InserPosDouLinkList(DouLinkList *list, DATATYPE *data,int pos)
{if(pos<0 ||pos>GetSizeDouLinkList(list)){fprintf(stderr,"InserPosDouLinkList error,index error\n");return 1;}if(IsEmptyDouLinkList(list) || 0 == pos){return InsertHeadLinkList(list,data);}else{DouLinkNode* tmp = list->head;tmp= list->head;DouLinkNode* newnode = (DouLinkNode*)malloc(sizeof(DouLinkNode));if(NULL == newnode){perror("InserPosDouLinkList malloc");return 1;}memcpy(&newnode->data,data,sizeof(DATATYPE));newnode->prev = NULL;newnode->next = NULL;int i = pos-1;while(i--){tmp=tmp->next;}newnode ->prev = tmp;newnode->next = tmp->next;if(tmp->next){tmp->next->prev = newnode;}tmp->next = newnode;}list->clen++;return 0;
}
man.c
#include <stdio.h>
#include "doulink.h"
#include <string.h>
int findbyname(DATATYPE*data,void* arg)
{return (0 == strcmp(data->name,(char*)arg));
}
int findbyage(DATATYPE*data,void* arg)
{return data->age == *(int*)arg;
}
int main()
{DATATYPE data[5]={{"zhangsan",'m',20,70},{"lisi",'f',21,60},{"wangmazi",'m',25,80},{"liubei",'f',30,85},{"caocao",'f',40,90},};DouLinkList* dl = CreateDouLinkList();InsertHeadLinkList(dl,&data[0]);InsertHeadLinkList(dl,&data[1]);InsertHeadLinkList(dl,&data[2]);ShowDouLinkList(dl,DIR_FORWARD);printf("-------------back---------------\n");ShowDouLinkList(dl,DIR_BACKWARD);printf("-------------find---------------\n");// char want_name[]="lisi";// //DouLinkNode* tmp = FindLinkList(dl,findbyname,want_name);// int want_age = 25;// DouLinkNode* tmp = FindLinkList(dl,findbyage,&want_age);// if(NULL == tmp)// {// printf("can't find person ,name:%s\n",want_name);// }// else// {// printf("%s:%d\n",tmp->data.name,tmp->data.score);// }// RevertDouLinkList(dl);// printf("-------------rev---------------\n");// ShowDouLinkList(dl,DIR_FORWARD);// DeleteLinkList(dl,findbyname,"lisi");// printf("-------------del forware---------------\n");// ShowDouLinkList(dl,DIR_FORWARD);// printf("-------------back---------------\n");// ShowDouLinkList(dl,DIR_BACKWARD);// ModifyDouLinkList(dl,findbyname,"zhangsan",&data[3]);// printf("-------------modify---------------\n");// ShowDouLinkList(dl,DIR_FORWARD);InserPosDouLinkList(dl,&data[3],3);printf("-------------pos---------------\n");ShowDouLinkList(dl,DIR_FORWARD);printf("-------------back---------------\n");ShowDouLinkList(dl,DIR_BACKWARD);DestroyDouLinkList(&dl);printf("Hello World!\n");return 0;
}
顺序表和链表 优缺点
顺序表(Array)
优点:
- 随机访问:顺序表支持通过索引快速访问任意位置的元素,时间复杂度为O(1)。
- 存储密度高:顺序表在物理存储上是连续的,存储密度大(即存储空间利用率高,因为不需要额外存储指针等信息)。
- 缓存友好:顺序表的数据在物理上连续存储,因此可能更好地利用CPU缓存,提高访问效率。
缺点:
- 插入和删除操作成本高:在顺序表的中间或开始位置插入或删除元素时,需要移动大量的元素来保持数据的连续性,时间复杂度为O(n)。
- 扩容问题:当顺序表的容量不足以存储更多元素时,需要进行扩容操作,这涉及到申请新的内存空间、复制原有数据到新空间等步骤,可能会比较耗时。
- 空间利用率可能不高:在顺序表中,如果预留的空间过大,但实际存储的元素较少,会导致空间浪费;如果预留的空间过小,又需要频繁扩容,影响效率。
链表(LinkedList)
优点:
- 插入和删除操作灵活:链表在插入和删除元素时,只需要改变指针的指向,不需要移动大量的元素,时间复杂度为O(1)(在已知位置进行操作时)。这使得链表非常适合于频繁插入和删除操作的场景。
- 动态分配内存:链表中的节点可以动态地申请和释放内存,使得链表的大小可以根据需要动态变化,无需担心空间浪费或扩容问题。
缺点:
- 访问元素效率低:访问链表中的元素需要从头节点开始遍历,直到找到所需的元素,时间复杂度为O(n)。
- 空间利用率相对较低:链表中每个节点除了存储数据本身外,还需要额外存储指针(或引用)信息,这增加了存储空间的开销。
- 缓存不友好:由于链表的节点在物理上不一定连续存储,因此可能无法有效地利用CPU缓存,导致访问效率下降。
循环链表
简单的来说,就是将原来单链表中最有一个元素的next指针指向第一个元素或头结点,链表就成了一个环,头尾相连,就成了循环链表。circultlar linker list.
注意非空表,和空表。多数会加入头结点。
原来结束的条件是
p->next != NULL ------->>>>> p-next != Head
或者写成 指定长度clen;
双向链表
double link list
typedef struct DulNode
{
ElemType date;
struct DulNode *pri;
sturct DulNode *next;
}DulNode,*DuLinkList;
相关文章:
Linux嵌入式学习——数据结构——线性表的链式结构
线性表的链式存储 解决顺序存储的缺点,插入和删除,动态存储问题。 特点: 线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占…...
文本编辑 文本中的各种空格
参考资料 欧文の半角スペースは2種類ある!?无中断空格常见空格一览浅析什么是零宽度字符以及零宽度字符在实际中的应用场景空格象形字间隔无中断空格零宽间隔 目录 零. 各种空格在Notepad中的效果一. 半角空格二. 全角空格三. TAB空格四. 无中断空格4.1 定义4.2 H…...
Vue插槽 (Slots)详解
目录 前言基础插槽具名插槽作用域插槽默认插槽动态插槽名总结相关阅读 前言 Vue的插槽(Slots)是一个非常强大的特性,它允许你在组件的模板中嵌入父组件的内容。插槽使得组件之间的内容分发变得灵活,尤其在构建可复用组件时非常…...
Unity中有关Animation的一点笔记
也许更好的阅读体验 Animation Unity中Animation类并不是直接记载了和播放动画有关的信息,可以简单理解Animation为一个动画播放器,播放的具体内容就像卡带一样,当我们有了卡带后我们可以播放动画。 对应的则是编辑器中的组件 所以Anima…...
module federation模块联邦与微前端
module federation是什么 webpack5新增了module federation,module federation的作用,将每个构建(build)作为容器(这是一个概念),构建后的资源可以正常部署,同时还具备在运行时对外暴露其中的模块,这就意味着多个构建…...
日常开发记录分享——C#控件ToolTip实现分栏显示内容
文章目录 需求来源实现思路实施请看VCR等等别走,有优化 需求来源 需要在鼠标浮动到指定位置后提示出详细的信息,一开始使用的tooltip实现,但是里面的内容效果并不理想,需要有条理性,于是就想到能不能将展示的东西分列…...
Kettle下载安装
环境说明 虚拟机:Win7;MySql8.0 主机:Win11;JDK1.8;Kettle 9.4(Pentaho Data Integration 9.4)(下载方式见文末) 安装说明 【1】解压后运行Spoon.bat 【2】将jar包 复…...
最新版Golang pprof使用(引入、抓取、分析,图文结合)
最新版Golang pprof使用 🔥具体实践: Go调试神器pprof使用教程Golang线上内存爆掉问题排查(pprof) Github地址:https://github.com/ziyifast/ziyifast-code_instruction/tree/main/go-demo/go-pprof 引入pprof:import _ “net/http/pprof” …...
vue3学习记录1:emit的写法
emit是用于child组件向parent组件通信的工具,因为vue3的script可以设置为setup,写法同vue2有较大区别。 一、script setup - 直接写 <script lang"ts" setup>const emit defineEmits([close]);function handleClose() {emit(close);}…...
Visual Studio Code + vue快速安装配置Node.js+Vue+webpack+vscode
第一部分:Node.js 第一步:下载Node.js 方法1:链接 下载 | Node.js 中文网 (nodejs.cn) 方法2:百度网盘 链接:https://pan.baidu.com/s/1zIqu8H9rb_I1i-1OWD7swQ?pwdaurk 提取码:aurk --来自百度网盘…...
【Dart 教程系列第 49 篇】什么是策略设计模式?如何在 Dart 中使用策略设计模式
这是【Dart 教程系列第 49 篇】,如果觉得有用的话,欢迎关注专栏。 博文当前所用 Flutter SDK:3.22.1、Dart SDK:3.4.1 文章目录 一:什么是策略设计模式?二:为什么要使用策略设计模式࿱…...
BGP路由反射器
原理概述 缺省情况下,路由器从它的一个 IBGP对等体那里接收到的路由条目不会被该路由器再传递给其他IBGP对等体,这个原则称为BGP水平分割原则,该原则的根本作用是防止 AS内部的BGP路由环路。因此,在AS内部,一般需要每台…...
DolphinDB Web 端权限管理:可视化操作指南
在现代数据库管理中,高效和直观的权限管理对于用户的数据安全是至关重要的。过去 DolphinDB 用户需要依赖系统脚本来管理用户和权限,这对于缺乏技术背景的管理员来说既复杂又容易出错。 为了提升用户体验和操作效率,DolphinDB 目前在 Web 上…...
学习Vue2收藏这一篇就够了(如何创建Vue实例)
什么是Vue? Vue是什么:是一个用于构建用户界面的渐进式框架 什么是构建用户界面:基于数据动态渲染页面 什么是渐进式:循序渐进的学习 什么是框架:一整套完整的项目解决方案 创建Vue实例 核心步骤(4步…...
Mysql数据库第四次作业
mysql> create table student(sno int primary key auto_increment,sname varchar(30) not null unique,Ssex varchar(2) check (Ssex男 or Ssex女) not null,Sage int not null,Sdept varchar(10) default计算机 not null); mysql> create table Course(Con int primar…...
使用Docker搭建MySql的主从同步+ShardingSphere搭建Mysql的读写分离
参考课程 尚硅谷ShardingSphere5实战教程(快速入门掌握核心)_哔哩哔哩_bilibili 主服务器 创建容器 docker run -d \ -p 3306:3306 \ -v /kira/mysql/master/conf:/etc/mysql/conf.d \ -v /kira/mysql/master/data:/var/lib/mysql \ -e MYSQL_ROOT…...
数据结构:数据类型与抽象数据类型
数据类型与抽象数据类型 数据类型基本数据类型构造数据类型指针类型枚举类型 抽象数据类型(ADT)抽象数据类型的组成部分常见的抽象数据类型示例 数据类型与抽象数据类型的区别实现抽象数据类型的具体方式用数组实现栈用链表实现栈 总结 数据类型 数据类…...
西方逻辑史简介
西方逻辑史研究,对形式逻辑实现现代化,对加强西方哲学史研究,对开展科学方法论的研究都有重要意义。西方逻辑史一般被划分成古代、中世纪、现代三个历史时期。本文拟对这三个时期中的七个重要逻辑学家和逻辑学派:亚里士多德、斯多…...
【论文10】复现代码tips
一、准备工作 1.创建一个虚拟环境 conda create --name drgcnn38 python=3.8.18 2.激活虚拟环境 conda activate drgcnn38 注意事项 在Pycharm中终端(terminal)显示PS而不是虚拟环境base 问题如下所示 解决方法:shell路径改成cmd.exe 重启终端显示虚拟环境 3.安装torch …...
分布式缓存获取以及设置
1. 通用代码 public SysUser getCache(String sysUserId) {String cacheKey "litgery:warehouse:" sysUserId;// 尝试从缓存中获取数据CacheData cacheData redisUtils.get(cacheKey);if (null ! cacheData) {if (Boolean.TRUE.equals(cacheData.getExist())) {re…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...
结构化文件管理实战:实现目录自动创建与归类
手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题,进而引发后续程序异常。使用工具进行标准化操作,能有效降低出错概率。 需要快速整理大量文件的技术用户而言,这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB,…...
高端性能封装正在突破性能壁垒,其芯片集成技术助力人工智能革命。
2024 年,高端封装市场规模为 80 亿美元,预计到 2030 年将超过 280 亿美元,2024-2030 年复合年增长率为 23%。 细分到各个终端市场,最大的高端性能封装市场是“电信和基础设施”,2024 年该市场创造了超过 67% 的收入。…...



