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

文章目录

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

一、前言

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

相关推荐
一只修仙的猿4 小时前
再谈性能优化,一次项目优化经历分享
android·性能优化
特立独行的猫a4 小时前
强大的鸿蒙HarmonyOS网络调试工具PageSpy 介绍及使用
网络·华为·harmonyos
ChinaDragon4 小时前
HarmonyOS:在NDK工程中使用预构建库
harmonyos
雮尘5 小时前
Android性能优化之枚举替代
android
2501_915909067 小时前
苹果上架App软件全流程指南:iOS 应用发布步骤、App Store 上架流程、uni-app 打包上传与审核技巧详解
android·ios·小程序·https·uni-app·iphone·webview
2501_915921437 小时前
iOS 文件管理与能耗调试结合实战 如何查看缓存文件、优化电池消耗、分析App使用记录(uni-app开发与性能优化必备指南)
android·ios·缓存·小程序·uni-app·iphone·webview
程序员潘Sir8 小时前
鸿蒙应用开发从入门到实战(三):第一个鸿蒙应用
harmonyos·鸿蒙
2501_915918418 小时前
App 苹果 上架全流程解析 iOS 应用发布步骤、App Store 上架流程
android·ios·小程序·https·uni-app·iphone·webview
安卓开发者8 小时前
鸿蒙NEXT中SQLite数据库全面实战指南
数据库·sqlite·harmonyos