文章目录
- 前言
- 链表的组成
- 链表的操作
- 总结
一、前言
鸿蒙系统的源码主要是c/c++语言,因为很多驱动的开发,都是要依赖c/c++语言的,另外鸿蒙系统有别于android 的最直观的体现就是,android 框架层很多是java写的,而鸿蒙依然是c/c++来编写。
所以如果要看懂这么庞大的系统源码,那么一定要看懂c/c++语言。
不得不说,原本是java,或者是js,ts的,再或者python的开发者,对于c/c++语言,还是有些胆怯的,因为它确实有点门槛,但是怎么说呢,孰能生巧。
c/c++ 虽然难懂,但是它在性能上是其他语言无法比拟的,这也就是为什么很多底层的东西,比如系统的,硬件方面,内存要求高的,基本都得靠它了。
当你开始接触c/c++,接触硬件,你对数据结构以及算法的重要性,会体会更深。
今天主要分享一下,最核心也是最基础的c/c++ 语言中的代码,可以说,c/c++语言,只要搞懂了指针,内存操作,那么基本就能很好的看懂代码了,这也是有别于其他语言的关键。
当然,今天分享的例子是为了更好的看懂鸿蒙系统源码服务的,太基础的就不说了,主要还是跟源码相关性比较强的,拿出来分析一下。
在源码中,很多地方都会涉及链表演变的操作。也是这个原因,所以内容主要围绕链表+指针。看懂了这个,对于看源码会轻松很多。另外后期也会分享更多鸿蒙源码相关的分析。
二、链表的组成部分
对于一个链表的操作主要涉及这么几个部分:
cpp
1、结构体
2、创建新的节点
3、链表头部插入节点
4、链表尾部插入节点
5、指定位置插入节点
6、删除指定位置节点
7、查找某个节点
8、打印链表
9、获取链表长度
10、释放链表内存
11、执行并验证
以上内容看起来很多,但其实要实现的代码并不多,都有注释,一个个看,会有不少收获,接下来,我们就一步步来实现。
三、链表的操作
1、结构体
在java 中,我们一般称之为实体类,在c/c++ 中我们称之为结构体,这么去理解,就简单很多
cpp
//定义链表节点结构体
struct Node{
int data;
struct Node* next; //指针声明
};
//指针:
//1、是一个变量,用于存储内存地址。(指向了内存,就能够很方便的进行值的操作,或者传递)
//2、指针有类型。它指向特定类型的数据,如(int* char*)
2、创建节点
malloc 主要用来分配内存的
cpp
//创建新节点
struct Node* createNode(int data){
//通过 malloc 为Node 分配内存
struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("内存分配失败\n");
exit(1); //分配失败时退出程序
}
newNode->data = data; //将传入的data值赋值给新节点的data字段
newNode->next = NULL; //将next 设为NULL,表示这是链表的最后一个节点
return newNode;//返回新建的节点
}
//->,这个箭头可以理解成java中的点"." 操作,相当于调用这个属性,这里的意思是对这个data属性进行赋值。
3、链表头部插入节点
这个函数就比较特殊,传参中使用了二级指针。第一次看可能会懵,不用怕,下面代码里头有解释。
cpp
//在链表头部插入节点(将新节点的next 指向头节点,这样就拼起来了,然后再更新头节点)
void insertAtHead(struct Node** head, int data){//二级指针
struct Node* newNode = createNode(data);
newNode->next = *head; //将新节点的下一节点 指向当前链表的头节点
*head = newNode; //更新原来的头节点,这样一来,就汇总在一起(newNode + head)
}
//指针作为参数,记住一个口诀:要改谁,就传谁的地址。
//这里要改指针,所以就要传指针的地址。(一个星号代表是变量的地址,两个星号就变成的指针的指针,也就是指针的地址)
4、链表尾部插入节点
cpp
//在链表尾部插入节点
void insertAtTail(struct Node** head, int data){
struct Node* newNode = createNode(data);
if (*head == NULL) {//头节点为空,说明当前没有任何节点
*head = newNode;
return;
}
//如果链表有节点,那么一步步操作
struct Node* current = *head; //从头节点开始
//遍历链表找到最后一个节点
while (current->next != NULL) {
current = current->next; //不是最后一个节点,那么移动到下一个节点到当前节点,继续操作
}
//此时current->next = NULL,也就是找到最后节点了
current->next = newNode; //将新节点给到current的next中,作为current的下一节点,这样就插到最后了
}
5、指定位置插入节点
判断是不是在头部插入,如果是,那么就调用之前的头部插入节点函数。 如果不是,那么就找到指定位置,并插入
cpp
//在指定位置 插入节点
void insertAtPosition(struct Node** head, int data, int position)
{
if (position < 0) {
printf("位置无效\n");
return;
}
if (position == 0) {//如果是0,就相当于在头部插入节点,直接使用上面的头部插入节点函数
insertAtHead(head, data);
return;
}
//如果不是在头部插入节点,那就要先去把插入位置前一个节点找出来
struct Node* newNode = createNode(data);//先把要插入的data,做成节点
struct Node* current = *head;//将原来的头节点作为开始的节点
//找出目标节点的前一个节点
for (int i = 0; i < position -1 && current != NULL; i++) {
current = current->next; //如果没找到,继续往下一节点找
}
//如果发现目标节点的上一个节点是NULL,就说明插入的位置越界了
if (current == NULL) {
printf("位置超出链表长度\n");
free(newNode);
return;
}
//为了好理解,假设目标位置position 是2,上面只需要执行一次循环,这个时候current 就是 1 位置的节点(正好是目标位置2 的前一个节点)
newNode->next = current->next; //将current的下一节点(包含剩下的链条) 一起移交给 新节点的next(这样新节点就拥有了原节点的剩余链条)
current->next = newNode; // 然后再把新节点(包含剩余的链条),一起作为current的下一节点,这样整个链条就拼接好了(也就是所谓的指定节点插入)
//这里用个通俗的解释,就是老大哥把一帮小弟给到新人,新人接管这些小弟,然后这个新人再领着这帮小弟,一起跟随老大哥。
}
6、删除指定值的节点
根据传入的值,进行查找,并删除
cpp
//删除指定值的节点
void deleteNode(struct Node** head, int data){
if (*head == NULL) {
printf("链表为空\n");
return;
}
struct Node* temp = *head;
struct Node* prev = NULL;
//如果要删除的是头节点
if (temp != NULL && temp->data == data) {
*head = temp -> next; //取出头结点的next,赋值回头结点,就相当于把头结点删除(原节点被子节点替换了)
free(temp);
return;
}
//查找要删除的节点
while (temp != NULL && temp->data != data) {
prev = temp;
temp = temp->next; //如果不是目标节点,那么移到下一个节点
}
if (temp == NULL) {
printf("未找到值为%d 的节点\n", data);
return;
}
//走到这里说明已经找到了目标节点的前一个节点。另外说明已经执行过 prev = temp了。
//找到目标节点,将它的下一个节点给到上面的节点,作为上面的子节点,这样当前节点就删除目标节点了
prev->next = temp->next; //把自己的小弟给到上面的大哥,这样自己就退出江湖了
free(temp);
//这个地方很多人,可能会觉得那为什么不是直接赋值给prev呢?下面举个例子就懂了:
//比如原来链表A->B->C
//假如 B是目标节点,那么相当于只需执行一次循环:
//prev = temp; 这时 prev 是 A 。 (因为temp = *head 而head 其实就是链表的头结点A)
//temp = temp->next; 到这里是将 A的下一节点给到temp ,也就是temp 是 B
//prev->next = temp->next; 把B的下一节点作为A的子节点,也就是A->C
//因为prev是A,所以temp->next 是赋值给prev-next,而不是prev,给prev,那相当于把A也给删除了。
}
7、查找某个节点
cpp
//查找某个节点
struct Node* searchNode(struct Node* head, int data){
struct Node* current = head;
while (current != NULL) {
if (current->data == data) {//找到了,就直接返回节点
return current;
}
current = current->next; //继续找
}
}
8、打印链表
cpp
//打印链表
void printList(struct Node* head){
struct Node* current = head;
printf("链表:");
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
9、获取链表的长度
cpp
//获取链表的长度
int getLength(struct Node* head){
int length = 0;
struct Node* current = head;
while (current != NULL) {
length++;
current = current->next;
}
return length;
}
10、释放链表
cpp
//释放链表内存
void freeList(struct Node** head){
struct Node* current = *head;
struct Node* next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
*head = NULL;
}
11、执行并验证
cpp
//主函数测试链表操作
int main(){
struct Node* head = NULL;
//插入节点
insertAtHead(&head, 10);
insertAtHead(&head, 20);
insertAtHead(&head, 30);
insertAtPosition(&head, 40, 1);
printList(head);
printf("链表长度:%d\n", getLength(head));
//查找节点
int serchValue = 30;
struct Node* foundNode = sercnNode(head, serchValue)
if (foundNode != NULL) {
printf("找到节点%d\n", searchValue);
}else{
printf("未找到节点%d\n", searchValue);
}
//删除节点
deleteNode(&head, 20);
printList(head);
//释放链表内存
freeList(&head);
return 0;
}
四、所有代码
1、以上部分的所有代码如下:
为了减少阅读负担,这部分的主要代码为主,用于运行测试,当你看不懂的时候,回头看文章中的解析,里面的注释更详细。
cpp
#include <stdio.h>
#include <stdlib.h>
//定义链表节点结构体
struct Node{
int data;
struct Node* next; //指针声明
};
//创建新节点
struct Node* createNode(int data){
//通过 malloc 为Node 分配内存
struct Node* newNode = (struct Node*) malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("内存分配失败\n");
exit(1); //分配失败时退出程序
}
newNode->data = data; //将传入的data值赋值给新节点的data字段
newNode->next = NULL; //将next 设为NULL,表示这是链表的最后一个节点
return newNode;//返回新建的节点
}
//在链表头部插入节点(将新节点的next 指向头节点,这样就拼起来了,然后再赋值回原链表中)
void insertAtHead(struct Node** head, int data){//二级指针
struct Node* newNode = createNode(data);
newNode->next = *head; //将新节点的next 指向当前链表的头节点
*head = newNode; //现在新节点成为链表的新的头节点
}
//在链表尾部插入节点
void insertAtTail(struct Node** head, int data){
struct Node* newNode = createNode(data);
if (*head == NULL) {//头节点为空,说明当前没有任何节点
*head = newNode;
return;
}
//如果链表有节点,那么一步步操作
struct Node* current = *head; //从头节点开始
//遍历链表找到最后一个节点
while (current->next != NULL) {
current = current->next; //不是最后一个节点,那么移动到下一个节点到当前节点,继续操作
}
//此时current->next = NULL,也就是找到最后节点了
current->next = newNode; //将新节点给到current的next中,作为current的下一节点,这样就插到最后了
}
//在指定位置 插入节点
void insertAtPosition(struct Node** head, int data, int position)
{
if (position < 0) {
printf("位置无效\n");
return;
}
if (position == 0) {//如果是0,就相当于在头部插入节点,直接使用上面的头部插入节点函数
insertAtHead(head, data);
return;
}
//如果不是在头部插入节点,那就要先去把插入位置前一个节点找出来
struct Node* newNode = createNode(data);//先把要插入的data,做成节点
struct Node* current = *head;//将原来的头节点作为开始的节点
//找出目标节点的前一个节点
for (int i = 0; i < position -1 && current != NULL; i++) {
current = current->next; //如果没找到,继续往下一节点找
}
//如果发现目标节点的上一个节点是NULL,就说明插入的位置越界了
if (current == NULL) {
printf("位置超出链表长度\n");
free(newNode);
return;
}
//为了好理解,假设目标位置position 是2,上面只需要执行一次循环,这个时候current 就是 1 位置的节点(正好是目标位置2 的前一个节点)
newNode->next = current->next; //将current的下一节点(包含剩下的链条) 一起移交给 新节点的next(这样新节点就拥有了原节点的剩余链条)
current->next = newNode; // 然后再把新节点(包含剩余的链条),一起作为current的下一节点,这样整个链条就拼接好了(也就是所谓的指定节点插入)
}
//删除指定值的节点
void deleteNode(struct Node** head, int data){
if (*head == NULL) {
printf("链表为空\n");
return;
}
struct Node* temp = *head;
struct Node* prev = NULL;
//如果要删除的是头节点
if (temp != NULL && temp->data == data) {
*head = temp -> next; //取出头结点的next,赋值回头结点,就相当于把头结点删除(原节点被子节点替换了)
free(temp);
return;
}
//查找要删除的节点
while (temp != NULL && temp->data != data) {
prev = temp;
temp = temp->next; //如果不是目标节点,那么移到下一个节点
}
if (temp == NULL) {
printf("未找到值为%d 的节点\n", data);
return;
}
//走到这里说明已经找到了目标节点的前一个节点。另外说明已经执行过 prev = temp了。
//找到目标节点,将它的下一个节点给到上面的节点,作为上面的子节点,这样当前节点就删除目标节点了
prev->next = temp->next; //把自己的小弟给到上面的大哥,这样自己就退出江湖了
free(temp);
}
//查找某个节点
struct Node* searchNode(struct Node* head, int data){
struct Node* current = head;
while (current != NULL) {
if (current->data == data) {//找到了,就直接返回节点
return current;
}
current = current->next; //继续找
}
}
//打印链表
void printList(struct Node* head){
struct Node* current = head;
printf("链表:");
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
//获取链表的长度
int getLength(struct Node* head){
int length = 0;
struct Node* current = head;
while (current != NULL) {
length++;
current = current->next;
}
return length;
}
//释放链表内存
void freeList(struct Node** head){
struct Node* current = *head;
struct Node* next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
*head = NULL;
}
//主函数测试链表操作
int main(){
struct Node* head = NULL;
//插入节点
insertAtHead(&head, 10);
insertAtHead(&head, 20);
insertAtHead(&head, 30);
insertAtPosition(&head, 40, 1);
printList(head);
printf("链表长度:%d\n", getLength(head));
//查找节点
int searchValue = 30;
struct Node* foundNode = searchNode(head, searchValue);
if (foundNode != NULL) {
printf("找到节点%d\n", searchValue);
}else{
printf("未找到节点%d\n", searchValue);
}
//删除节点
deleteNode(&head, 20);
printList(head);
//释放链表内存
freeList(&head);
return 0;
}
将以上代码放到编译器执行,或者在线编辑器 www.jyshare.com/compile/11/ 即可得到以下结果。
2、运行结果:

以上就是链表的操作,链表重点是删除和插入, 要多多练习,会体会更深刻。
最后献上我的个人v,专属终身顾问,解答关于鸿蒙相关的东西,包括项目中的问题,还有最新的技术点,快速开发,抢在前头。hmssz1
技术迭代很快,不要在一个问题上拔不出来,找到专业的人,快速的解决,作为程序员时间是最宝贵的,不要自己蒙头苦干,等你搞懂了,这个技术已经过时了。
总结
1、介绍c/c++在鸿蒙系统源码中的重要性
2、链表组成以及操作
3、验证链表的操作
如果对你有一点点帮助,那是值得高兴的事情。:)
我的csdn:blog.csdn.net/shenshizhon...