【数据结构】手把手教你单链表(c语言)(附源码)

🌟🌟作者主页:ephemerals__

🌟🌟所属专栏:数据结构

目录

前言

1.单链表的概念与结构

2.单链表的结构定义

3.单链表的实现

3.1 单链表的方法声明

3.2 单链表方法实现

3.2.1 打印链表

3.2.2 创建新节点

3.2.3 尾插

3.2.4 头插

3.2.5 尾删

3.2.6 头删

3.2.7 查找

3.2.8 指定位置之前插入

3.2.9 指定位置之后插入

3.2.10 删除指定位置的节点

3.2.11 销毁链表

4.程序全部代码

总结


前言

        之前我们学习了顺序表,基于顺序表的结构和实现方式,它有以下缺陷

1.指定位置、头部的插入/删除的时间复杂度是O(N),效率并不是很高。

2.在增容时,需要申请额外的空间,当连续的空间不足时,就需要重新开辟空间并且拷贝数据,消耗较大。

3.由于增容操作每次都是以2倍的形式增长,所以势必会造成一定的空间浪费。

如何解决以上问题呢?这就需要我们学习一个新的数据结构:单链表

1.单链表的概念与结构

链表的概念:链表是一种数据内存地址不连续、但是逻辑顺序连续的数据结构。它的逻辑顺序由链表中节点的指针相连接。

节点:由两部分组成:存储数据元素的部分称之为“数据域”,存放其他节点地址的部分称之为“指针域”。每一个数据元素存放于一个“节点”中。

单链表,也叫做单向链表,它的节点的指针域中存放的是下一个节点的地址。这样节点与节点之间互相连接,就像链条一样将数据串联起来。

单链表的结构如图:

可以看到,单链表就像火车一样,而每一个节点就相当于是一节车厢,它们之间用指针串联在一起。注意:单链表只能做到由前一个节点找到后一个几点,无法逆转;最后一个节点的指针域为空指针。

2.单链表的结构定义

        我们在定义单链表的结构时,定义的是它的节点的结构。代码如下:

typedef int SLTDataType;//定义单链表的节点
typedef struct SListNode
{SLTDataType data;//数据域struct SListNode* next;//指针域
}SLTNode;

可以看到,它的指针域是指向自己本身类型的指针,这种定义方式也叫做结构体的自引用。它可以使得该节点能够存放一个相同类型节点的地址,并且进行访问操作

3.单链表的实现

3.1 单链表的方法声明

        单链表的一些常用方法的声明如下:

//打印链表
void SLTPrint(SLTNode* phead);//创建新节点
SLTNode* SLTBuyNode(SLTDataType n);//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType n);//头插
void SLTPushFront(SLTNode** pphead, SLTDataType n);//尾删
void SLTPopBack(SLTNode** pphead);//头删
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType n);//指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType n);//指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType n);//删除指定位置节点
void SLTErase(SLTNode** pphead, SLTNode* pos);//销毁链表
void SLTDestroy(SLTNode** pphead);

接下来,我们尝试逐一实现以上方法。

3.2 单链表方法实现

3.2.1 打印链表

        打印链表时,我们需要定义一个指针,通过它遍历链表并访问它的数据元素:

//打印链表
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;//定义指针指向头节点while (cur != NULL)//最后一个节点的next为空,cur等于空则说明遍历结束{printf("%d ", cur->data);//访问数据并打印cur = cur->next;//对cur解引用拿到下一个节点的地址,然后赋值给cur,cur就指向了下一个节点}printf("\n");
}

这里我们需要注意理解语句“cur = cur->next”,由于next存放的是下一个节点的地址,所以将其赋值给cur,cur就指向了下一个节点,循环往复,就达到了遍历的效果。

3.2.2 创建新节点

        在我们进行元素插入操作时,往往要将数据存放在一个节点当中,然后将这个节点插入链表。所以我们将创建节点的操作封装成一个函数:

//创建新节点
SLTNode* SLTBuyNode(SLTDataType n)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//动态开辟一个节点大小的内存if (newnode == NULL)//内存开辟失败,则直接退出程序{perror("malloc");exit(1);}newnode->data = n;//将数据赋值给节点的数据域newnode->next = NULL;//为了确保链表末尾为空指针,所以创建的所有节点默认next为空return newnode;//将节点返回
}

3.2.3 尾插

        接下来我们学习尾插操作。既然要在链表尾部插入数据,那么就需要我们顺着链表的头节点找到尾部的节点,然后将其指针域指向我们的新节点就好。代码如下:

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType n)
{assert(pphead);SLTNode* newnode = SLTBuyNode(n);//创建新节点if (*pphead == NULL)//头指针为空说明链表为空{//链表为空,此时插入第一个元素,需要将头指针指向新节点,//而在函数内修改头指针就要传入头指针的地址,也就是二级指针*pphead = newnode;}else//链表不为空的情况{SLTNode* cur = *pphead;while (cur->next != NULL)//从头节点开始,循环遍历找到最后一个节点{cur = cur->next;}cur->next = newnode;//将新节点的地址赋值给最后一个节点的指针域}
}

这里需要注意:当链表为空时,如果我们进行循环遍历,就会发生对空指针解引用的错误,所以直接使头指针指向新节点就好。由于要在函数体内改变参数的值,并且参数是一个一级指针变量,所以要传入一级指针的地址,也就是二级指针。

3.2.4 头插

        对于头插操作,我们需要将新节点的next指向原来的第一个节点,然后将头指针指向新节点。

我们画图表示一下:

代码实现:

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType n)
{assert(pphead);//确保传入的不是空指针SLTNode* newnode = SLTBuyNode(n);//创建新节点newnode->next = *pphead;//使新节点的next指针指向原来的第一个节点*pphead = newnode;//头指针指向新节点
}

注意:最后两句代码的顺序不能颠倒,因为如果先让头指针指向新节点,原来的链表的地址就会丢失,无法访问到了。

3.2.5 尾删

        进行尾删操作时,我们也需要遍历链表,找到链表的末尾并释放内存。实际操作要做一些特殊情况和细节的处理:

//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//确保传入的不是空指针并且链表不为空if ((*pphead)->next = NULL)//链表只有一个节点的情况{free((*pphead)->next);//释放该节点的空间*pphead == NULL;//改变了头节点的值,所以也要传二级指针}else//节点大于1的情况{SLTNode* prev = *pphead;while (prev->next->next != NULL)//循环遍历,使prev指向倒数第二个节点{prev = prev->next;}free(prev->next);//释放最后一个节点的空间prev->next = NULL;//将此时的最后一个节点的next制为空}
}

3.2.6 头删

        对于头删操作,我们需要记录第二个节点,然后再将第一个节点释放,最后使头指针指向记录的节点即可。当链表只有一个节点时,我们记录的就是NULL,最后将NULL赋值给头指针也合情合理。无需分类讨论。

//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;//保存第二个节点的地址/空指针(只有一个节点时)free(*pphead);//释放第一个节点的空间*pphead = next;//让头指针指向刚才保存的节点/空指针,也要传二级指针
}

3.2.7 查找

        查找操作十分简单,只需要遍历链表,如果有匹配的节点,将其地址返回即可。

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType n)
{SLTNode* cur = phead;while (cur != NULL)//遍历链表的所有节点{if (cur->data == n)//匹配成功,返回该节点的地址{return cur;}cur = cur->next;}return NULL;//没有找到,返回空指针
}

3.2.8 指定位置之前插入

        进行指定位置之前插入时,要进行分类讨论:如果指定位置是头节点,则进行头插;其他情况遍历找到该节点的前驱节点prev,然后进行插入操作:

代码实现:

//指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType n)
{assert(pphead && pos);SLTNode* newnode = SLTBuyNode(n);if (*pphead == pos)//指定位置是头节点的情况{//进行头插newnode->next = *pphead;*pphead = newnode;}else{SLTNode* prev = *pphead;while (prev->next != pos)//遍历找到pos节点的前驱节点{prev = prev->next;}newnode->next = pos;//新节点的next指针指向posprev->next = newnode;//前驱节点的next指针指向新节点}
}

3.2.9 指定位置之后插入

        对于指定位置之后插入元素,由于已经找到了前驱节点和后继节点,相比就没有那么麻烦了,只需要直接插入即可。

代码实现:

//指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType n)
{assert(pos);//确保pos不为空指针SLTNode* newnode = SLTBuyNode(n);newnode->next = pos->next;//newnode的next指向后继节点pos->next = newnode;//前驱节点的next指向newnode
}

3.2.10 删除指定位置的节点

        对于指定位置的删除,我们需要分类讨论:如果此位置是头节点,就进行头删;否则就要找到其前驱节点和后继节点,然后进行删除操作。

//删除指定位置节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos && *pphead);//确保传入的不是空指针并且链表不为空if (pos == *pphead)//指定位置是头节点情况{//头删*pphead = pos->next;//先使头指针指向第二个节点free(pos);//释放掉pos节点}else{SLTNode* prev = *pphead;while (prev->next != pos)//循环遍历,找到pos的前驱节点{prev = prev->next;}prev->next = pos->next;//使前驱节点的next指针指向pos的后继节点free(pos);//释放掉pos节点}pos = NULL;//对野指针及时制空
}

3.2.11 销毁链表

        当我们使用完链表之后,应当及时释放掉链表的所有节点内存,这个过程称之为销毁链表。代码如下:

//销毁链表
void SLTDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* cur = *pphead;//从头节点开始遍历while (cur != NULL){SLTNode* next = cur->next;//先记录下一个节点free(cur);//释放当前节点cur = next;//释放后,cur指向记录的节点}*pphead = NULL;//将头指针制空
}

4.程序全部代码

        程序全部代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int SLTDataType;//定义单链表的节点
typedef struct SListNode
{SLTDataType data;//数据域struct SListNode* next;//指针域
}SLTNode;//打印链表
void SLTPrint(SLTNode* phead);//创建新节点
SLTNode* SLTBuyNode(SLTDataType n);//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType n);//头插
void SLTPushFront(SLTNode** pphead, SLTDataType n);//尾删
void SLTPopBack(SLTNode** pphead);//头删
void SLTPopFront(SLTNode** pphead);//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType n);//指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType n);//指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType n);//删除指定位置节点
void SLTErase(SLTNode** pphead, SLTNode* pos);//销毁链表
void SLTDestroy(SLTNode** pphead);//打印链表
void SLTPrint(SLTNode* phead)
{SLTNode* cur = phead;//定义指针指向头节点while (cur != NULL)//最后一个节点的next为空,cur等于空则说明遍历结束{printf("%d ", cur->data);//访问数据并打印cur = cur->next;//对cur解引用拿到下一个节点的地址,然后赋值给cur,cur就指向了下一个节点}printf("\n");
}//创建新节点
SLTNode* SLTBuyNode(SLTDataType n)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//动态开辟一个节点大小的内存if (newnode == NULL)//内存开辟失败,则直接退出程序{perror("malloc");exit(1);}newnode->data = n;//将数据赋值给节点的数据域newnode->next = NULL;//为了确保链表末尾为空指针,所以创建的所有节点默认next为空return newnode;//将节点返回
}//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType n)
{assert(pphead);SLTNode* newnode = SLTBuyNode(n);//创建新节点if (*pphead == NULL)//头指针为空说明链表为空{//链表为空,此时插入第一个元素,需要将头指针指向新节点,//而在函数内修改头指针就要传入头指针的地址,也就是二级指针*pphead = newnode;}else//链表不为空的情况{SLTNode* cur = *pphead;while (cur->next != NULL)//从头节点开始,循环遍历找到最后一个节点{cur = cur->next;}cur->next = newnode;//将新节点的地址赋值给最后一个节点的指针域}
}//头插
void SLTPushFront(SLTNode** pphead, SLTDataType n)
{assert(pphead);//确保传入的不是空指针SLTNode* newnode = SLTBuyNode(n);//创建新节点newnode->next = *pphead;//使新节点的next指针指向原来的第一个节点*pphead = newnode;//头指针指向新节点
}//尾删
void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);//确保传入的不是空指针并且链表不为空if ((*pphead)->next = NULL)//链表只有一个节点的情况{free((*pphead)->next);//释放该节点的空间*pphead == NULL;//改变了头节点的值,所以也要传二级指针}else//节点大于1的情况{SLTNode* prev = *pphead;while (prev->next->next != NULL)//循环遍历,使prev指向倒数第二个节点{prev = prev->next;}free(prev->next);//释放最后一个节点的空间prev->next = NULL;//将此时的最后一个节点的next制为空}
}//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;//保存第二个节点的地址/空指针(只有一个节点时)free(*pphead);//释放第一个节点的空间*pphead = next;//让头指针指向刚才保存的节点/空指针,也要传二级指针
}//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType n)
{SLTNode* cur = phead;while (cur != NULL)//遍历链表的所有节点{if (cur->data == n)//匹配成功,返回该节点的地址{return cur;}cur = cur->next;}return NULL;//没有找到,返回空指针
}//指定位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType n)
{assert(pphead && pos);SLTNode* newnode = SLTBuyNode(n);if (*pphead == pos)//指定位置是头节点的情况{//进行头插newnode->next = *pphead;*pphead = newnode;}else{SLTNode* prev = *pphead;while (prev->next != pos)//遍历找到pos节点的前驱节点{prev = prev->next;}newnode->next = pos;//新节点的next指针指向posprev->next = newnode;//前驱节点的next指针指向新节点}
}//指定位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType n)
{assert(pos);//确保pos不为空指针SLTNode* newnode = SLTBuyNode(n);newnode->next = pos->next;//newnode的next指向后继节点pos->next = newnode;//前驱节点的next指向newnode
}//删除指定位置节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && pos && *pphead);//确保传入的不是空指针并且链表不为空if (pos == *pphead)//指定位置是头节点情况{//头删*pphead = pos->next;//先使头指针指向第二个节点free(pos);//释放掉pos节点}else{SLTNode* prev = *pphead;while (prev->next != pos)//循环遍历,找到pos的前驱节点{prev = prev->next;}prev->next = pos->next;//使前驱节点的next指针指向pos的后继节点free(pos);//释放掉pos节点}pos = NULL;//对野指针及时制空
}//销毁链表
void SLTDestroy(SLTNode** pphead)
{assert(pphead);SLTNode* cur = *pphead;//从头节点开始遍历while (cur != NULL){SLTNode* next = cur->next;//先记录下一个节点free(cur);//释放当前节点cur = next;//释放后,cur指向记录的节点}*pphead = NULL;//将头指针制空
}

总结

        相比于顺序表,单链表采用了不同的物理结构,这使得头插、头删等操作的效率高于顺序表,并且插入一个数据就会创建一个节点,避免了空间的浪费

        学习单链表是数据结构中相当重要的一个环节,学会了单链表,才会更容易地理解其他数据结构的底层逻辑。我们在学习数据结构时,要注意勤画图,勤调试,才能让我们的编程能力更上一层楼。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1488679.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

机械学习—零基础学习日志(高数11——三角函数)

零基础为了学人工智能&#xff0c;真的开始复习高数 三角函数之所以比较困难&#xff0c;是因为过于抽象&#xff0c;距离生活太过遥远&#xff0c;这里搜集一些资料&#xff0c;帮助大家能加深对三角函数的理解。 三角函数作用——能测距离 三角函数从应用层&#xff0c;开…

C++ | Leetcode C++题解之第287题寻找重复数

题目&#xff1a; 题解&#xff1a; class Solution { public:int findDuplicate(vector<int>& nums) {int slow 0, fast 0;do {slow nums[slow];fast nums[nums[fast]];} while (slow ! fast);slow 0;while (slow ! fast) {slow nums[slow];fast nums[fast]…

RuoYi基于SpringBoot+Vue前后端分离的Java快速开发框架学习_2_登录

文章目录 一、登录1.生成验证码2.验证码作用1.大体流程2.代码层面(我们都是从前端开始看起) 一、登录 1.生成验证码 基本思路&#xff1a; 后端生成一个表达式&#xff0c;例如34?7,显而易见后面是答案截取出来题干和答案把题干11&#xff1f;变成图片&#xff0c;变成流&a…

【Qt】QLCDNumber和QProgressBar

目录 QLCDNumber 倒计时小程序 相关属性 QProgressBar 进度条小程序 相关设置 QLCDNumber QLCDNumber是Qt框架中用于显示数字或计数值的小部件。通常用于显示整数值&#xff0c;例如时钟、计时器、计数器等 常用属性 属性说明intValueQLCDNumber显示的初始值(int类型)va…

Python爬虫技术 第13节 HTML和CSS选择器

在爬虫技术中&#xff0c;解析和提取网页数据是核心部分。HTML 和 CSS 选择器被广泛用于定位网页中的特定元素。下面将详细介绍这些选择器如何在 Python 中使用&#xff0c;特别是在使用像 Beautiful Soup 或 Scrapy 这样的库时。 HTML 选择器 HTML 选择器基于 HTML 元素的属性…

uniapp手写滚动选择器

文章目录 效果展示HTML/Template部分&#xff1a;JavaScript部分&#xff1a;CSS部分&#xff1a;完整代码 没有符合项目要求的选择器 就手写了一个 效果展示 实现一个时间选择器的功能&#xff0c;可以选择小时和分钟&#xff1a; HTML/Template部分&#xff1a; <picker…

从食堂采购系统源码到成品:打造供应链采购管理平台实战详解

本篇文章&#xff0c;笔者将详细介绍如何从食堂采购系统的源码开始&#xff0c;逐步打造一个完备的供应链采购管理平台&#xff0c;帮助企业实现采购流程的智能化和高效化。 一、需求分析与规划 一般来说&#xff0c;食堂采购系统需要具备以下基本功能&#xff1a; 1.供应商…

【原创】java+swing+mysql理发店管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 开发背景&#xff1a; 随着社会经济的…

文件上传漏洞(ctfshow web151-161)

Web151 F12修改源代码 exts后面png改为php 这样就可以上传php的文件了 Web152&#xff1a; 考点&#xff1a;后端不能单一校验 就是要传图片格式&#xff0c;抓个包传个png的图片 然后bp抓包修改php后缀解析 然后放包 Web153-web156 在php代码中可以使用“{}”代替“[]” …

Nacos 高级详解:提升你的开发和部署效率

Nacos 高级 一 、服务集群 需求 服务提供者搭建集群 服务调用者&#xff0c;依次显示集群中各服务的信息 搭建 修改服务提供方的controller&#xff0c;打印服务端端口号 package com.czxy.controller;import org.springframework.web.bind.annotation.*;import javax.a…

Leetcode—240. 搜索二维矩阵 II【中等】

2024每日刷题&#xff08;149&#xff09; Leetcode—240. 搜索二维矩阵 II 实现代码 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int r 0;int c matrix[0].size() - 1;while(r < matrix.size() &&…

magento2 安装win环境和linux环境

win10 安装 安装前提&#xff0c;php,mysql,apach 或nginx 提前安装好 并且要php配置文件里&#xff0c;php.ini 把错误打开 display_errorsOn开始安装 检查环境 填写数据库信息 和ssl信息&#xff0c;如果ssl信息没有&#xff0c;则可以忽略 填写域名和后台地址&#xff0…

【NLP自然语言处理】为什么说BERT是bidirectional

首先&#xff0c;来看一下Transformer架构图&#xff1a; 我们知道&#xff0c;Bert设计时主要采用的是Transformer编码器部分&#xff0c;要论述Bert为啥是双向的&#xff0c;我想从编码器和解码器的注意力机制来阐述。 在看这篇博客前&#xff0c;需要对Transformer有一定的…

vite构建vue3项目hmr生效问题踩坑记录

vite构建vue3项目hmr生效问题踩坑记录 hmr的好处 以下是以表格形式呈现的前端开发中HMR&#xff08;热模块替换&#xff09;带来的好处&#xff1a; 好处描述提升开发效率允许开发者在不刷新整个页面的情况下实时更新修改的代码&#xff0c;减少等待时间保持应用状态在模块替…

Vue3与Element-plus配合 直接修改表格中的一项数据——控制输入框的显示与隐藏

利用控制与隐藏输入框,直接修改表格中的每一项数据。 <!-- 表格模块 --> <div><el-table :data"tablelist" style"width: 100%"><el-table-column align"center" prop"deposit" label"接单押金">&l…

关于promise的一些例题(运行步骤详细说明)

关于promise的一些例题(详细说明) 基本例题 // 直接运行 输出 1 2 const promise new Promise((resolve, reject) > {console.log(1);resolve();console.log(2); });// then后面放入微队列 promise.then(() > {console.log(3); });// 输出4 之后没有代码了所以运行为队…

【运算放大器】输入失调电压和输入偏置电流(2)实例计算

概述 根据上一篇文章的理论&#xff0c;分别计算没有输入电阻和有输入电阻两种情况下的运放总输出误差。例题来自于TI高精度实验室系列课程。 目录 概述实例计算 1&#xff1a;没有输入电阻实例计算 2&#xff1a;有输入电阻总结 实例计算 1&#xff1a;没有输入电阻 要求&am…

Jmeter三种方式获取数组中多个数据并将其当做下个接口参数入参【附带JSON提取器和CSV格式化】

目录 一、传统方式-JOSN提取器获取接口返回值 1、接口调用获取返回值 2、添加JSON提取器 3、调试程序查看结果 4、添加循环控制器 5、设置count计数器 6、添加请求 7、执行请求 二、CSV参数化 1、将结果写入后置处理程序 2、设置循环处理器 3、添加CSV文件 4、设置…

汉兴能源研发费用率下降,“不差钱”募集资金近九成补流?

《港湾商业观察》施子夫 王璐 日前&#xff0c;冲刺创业板的上海汉兴能源科技股份有限公司&#xff08;以下简称&#xff0c;汉兴能源&#xff09;更新了招股书。 2023年6月末&#xff0c;汉兴能源正式递表创业板&#xff0c;保荐机构为长江证券。 从业务属性上来看&#x…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十三章 设备树下的platform驱动

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…