美文网首页
[C指针]单链表:内存视角看待 addHead 函数的本质(内存

[C指针]单链表:内存视角看待 addHead 函数的本质(内存

作者: AkuRinbu | 来源:发表于2019-04-19 16:43 被阅读0次

    学习笔记

    [C指针]指针与结构体:完整源码 C语言 实现 单链表

    https://www.jianshu.com/p/160a239086ae

    addHead 源码

    void addHead(LinkedList * list, void * data) {
        Node * node = (Node *) malloc(sizeof(Node));
        node->data = data;
        if(list-> head == NULL) {
            list->tail = node;
            node->next = NULL;
        } else {
            node->next = list->head;
        }
        list->head = node;
    }
    
    
    • 注意:链表为空(还没有结点)不代表链表指针变量值是NULL,实际上,指针变量现在是什么都有可能,因此不能写list == NULL,函数initializeList有明明白白地将list-> head初始化成了NULL
    void initializeList(LinkedList * list) {
        list->head = NULL;
        list->tail = NULL;
    }
    

    addHead算法过程

    先给节点分配内存,然后把传递给函数的数据赋给结构体的data字段。通过把data以void指针的形式传递,链表就能够持有用户想用的任何类型的数据了。接下来,我们检查链表是否为空,如果为空,就把尾指针指向节点,然后把节点的next字段赋值为NULL;如果不为空,那么将节点的next指针指向链表头。无论哪种情况,链表头都指向节点:

    addHead 函数本质 内存示意图

    addHead 本质.png

    内存示意图说明

    • 首先,main函数内创建的变量,比如Employee *samuelEmployee *sallyEmployee *susan这些在程序结束之前都是一直存在的;

    • 其次,addHead内部创建的局部变量Node * node,在每次函数执行完毕后就不复再存在了,即每次函数调用时都会重新创建一个全新的Node * node

    • 回想一下,指针的本质的是什么?是一个变量,是一个存着地址的变量,对于Employee *samuel而言,它的变量名就是samuel,它的值就是samuel,这个值就是它存着的地址

    • 再回想一下,C语言的函数是靠什么传递的?是传递,是传递变量的值,那么addHead(&linkedList, samuel);其实就是addHead(&linkedList, 0x100);

    • 现在进入到addHead内部,每次都需要在内部创建一个局部变量node,对于Node * node = (Node *) malloc(sizeof(Node)); 这条语句一定要分成三部分来看待:

    第一部分是左边,创建了一个局部变量node,对应于示意图中绿色部分;

    第二部分是右边,动态分类了一个Node类型的内存空间,根据Node类型的定义可以知道,Node类型本质就是两个指针,一个void * data,一个 Node * next,指针本身的大小只与机器、编译器有关,而与指向的数据类型无关,因此不管是一个void * data,还是一个char * ptr ,在同一台机器、同一个编译器下,sizeof(data)sizeof(char) 的数值都代表的是指针的大小,在示意图中我假设指针大小是4字节,因此,datanext在连续的内存地址之下,表示为0x500、0x504这种;

    第三部分,其实就是等于号 =,相当于示意图中的绿色箭头,把动态分配的内存空间首地址赋值给了node变量,即把0x500填到了绿色框内

    node变量是函数局部变量,在函数执行完毕之后就不复存在,但是malloc分配的动态内存是一直存在的

    • 继续看源码,接下来是一句 node->data = data;

    首先,是语句右边的那个data,这是什么?这是函数的传入参数void addHead(LinkedList *list, void* data) {},前面说明过了,C语言是值传递,因此其实这个右边的data就是0x100,这条代码本质就是 node->data = 0x100;

    然后再看,语句左边那个node->data是什么?前面说了,动态分配malloc内存 0x500 ~ 0x504 ~ 0x507这段连续的内存申请了内存空间,作为一个Node类型,换言之,如果你想要得到0x500这个数值,写的是 &node->data ,注意前面这个 &符号;

    如果是 要修改0x500处的数据,就是用 node->data = 想写入的数据,注意,我们不是修改0x500,而是修改0x500处的数据,换言之,我们 想将0x100填入到0x500处

    • 之后,就是关于next的部分与data其实异曲同工,参见算法流程

    • 最后,还有一个地方,是一定要理解的,那就是,在函数addHead执行完毕后,正如示意图所示,有两个地方存着 0x100 这个数据:

    第一个地方,就是在函数addHead调用之前,最初那个Employee *samuel,这是一个指向Employee类型的指针,Employee *samuel = (Employee*) malloc(sizeof(Employee));,这同样是需要分三个部分:右边、左边、中间去看待的语句,并且我们知道真正存着那个有着name以及age的数据Employee 结构体是由malloc分配的,而0x100就是那个结构体的起始地址,在0x100开始那里才是存着真正的结构体;

    第二个地方,当我们函数addHead的时候,的确有把0x100以值传递的形式发送给函数的传入参数的,但是无论是参数还是局部变量在函数执行完后不复存在了,可是在函数执行过程中,我们有明明确确地把0x100写到了0x500处,因此这里也存着一个0x100

    • 分析内存的变化,真正理解什么是C语言的值传递,真正理解什么是malloc动态分配的内存,所谓的链表结点的连接无非就是内存某处写入一个地址

    相关文章

      网友评论

          本文标题:[C指针]单链表:内存视角看待 addHead 函数的本质(内存

          本文链接:https://www.haomeiwen.com/subject/xafcgqtx.html