看懂鸿蒙系统源码 比较重要的知识点

文章目录

  • 前言
  • 链表的组成
  • 链表的操作
  • 总结

一、前言

鸿蒙系统的源码主要是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...

我的掘金:juejin.cn/user/428855...

相关推荐
2501_916008894 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
我科绝伦(Huanhuan Zhou)5 小时前
MySQL一键升级脚本(5.7-8.0)
android·mysql·adb
怪兽20146 小时前
Android View, SurfaceView, GLSurfaceView 的区别
android·面试
龚礼鹏7 小时前
android 图像显示框架二——流程分析
android
消失的旧时光-19437 小时前
kmp需要技能
android·设计模式·kotlin
帅得不敢出门8 小时前
Linux服务器编译android报no space left on device导致失败的定位解决
android·linux·服务器
爱笑的眼睛118 小时前
HarmonyOS Want意图传递机制深度解析
华为·harmonyos
雨白8 小时前
协程间的通信管道 —— Kotlin Channel 详解
android·kotlin
TimeFine10 小时前
kotlin协程 容易被忽视的CompletableDeferred
android
爱笑的眼睛1111 小时前
HarmonyOS语音识别与合成:构建智能语音应用的深度指南
华为·harmonyos