• 【C/C++】动态内存分配和链表


    本文对链表以及C/C++中的动态链表做详细诠释。

    什么是链表?
      链表是一种重要的数据结构,它最大的优点是可以进行动态的存储分配。链表有单向链表,双向链表,循环链表。对于c,这里我们只讨论单向链表。
      我们知道,内存是由栈和堆组成的。栈空间是由操作系统和编译系统控制的,比如我们定义int a;这个a就是在栈中开辟内存单元的。而堆空间,则允许给用户提供了虚拟空间,          在堆中是没有变量名这个说法的,只能通过地址来找到内存中存放的东西。

      既然是动态内存分配,当然有动态分配的特殊方法。在c中是以函数的形式实现的。
    1.malloc函数
    函数原型:void *malloc(unsigned int size)
    函数的作用是:在内训的动态存储区开辟一个size个字节的连续空间,返回所分配区域的首字节地址。
    可以看到,函数返回值是一个void指针,请注意,void指针不是一个可以指向任何类型数据的指针,而是 说,不指向任何类型的数据,仅仅是提供了一个地址。
    因而,你想让这个指针指向int型数据,要进行显式的类型转换(强制类型转换),即在前面加(int *)。一般来说,如果不加,是可以自动进行隐式类型转换的。

    2.calloc函数
    函数原型:void *calloc(unsigned n,unsigned size)
    作用:开辟n个长度为size的连续空间。一般用来保存一个数组。
    3.realloc函数
    原型:void *realloc(void *p,unsigned int size)
    作用:用来重新分配已经分配的动态空间的大小。
    4.free函数
    原型:void free(void *p)
    作用:把已经分配的动态空间释放掉。

    ======================================================================

    言归链表:
      一个链表,是由许多节点组成的。可以自由的删除、添加。指向首节点的指针叫做head指针,它是链表的唯一标示。每个节点包含两部分,一是数据,而是下一个节点的地址。通过这个下个节点的地址,建立了整个链表的联系。

    如何建立一个链表呢?
      用结构体建立链表当然是再合适不过了。
      比如:现在建立一个静态链表。
      struct S
      {
        int a;
        struct S *next;
      }S1,S2,S3;
      struct S *head;
      S1.a=1;S2.a=2;S3.a=3;

      head=&S1;
      S1.next=&S2;
      S2.next=&S3;
      S3.next=NULL; //请注意让最有一个节点存放的地址为NULL。

      这样,我们就建立了一个有三个节点的静态链表。
      

    如何建立一个动态链表呢?又如何添加、删除节点,更进一步说,怎么使得链表有序,并且进行删除添加操作后使之依然有序 ?

    建立动态链表,就是可以随时根据需要来增加结点 。看代码:
      #include<stdio.h>
      #include<stdlib.h>
      #define LEN sizeof(struct S)
      struct S
      {
        int a;
        struct S *next;
      };
      int n; //全局变量n,记录节点个数。未赋初值默认为0;
      struct S *creat_autolist(void) //函数,来创建一个动态链表
      {
        struct S *head; //头指针;
        struct S *p0,*p1; //p0为当前节点;
        p0=p1=(struct S*)malloc(LEN);//用malloc函数开辟一个结点的空间 ;
        scanf("%d",&p0->a);//输入数据;
        head=NULL;
      while(p0->a!=0) //循环,输入的a不为0则继续输入;
      {
        n++;
        if(n==1) head=p0; //是否为第一个结点;
        else p1=p0->next;

        p1=p0; //让p1也指向p0所指向的结点
        p0= (struct S*)malloc(LEN); //在开辟一个节点;
        scanf("%d",&p0->a);
       }
       p1->next=NULL;//尾结点的指针部分为NULL
       return (head);
      }
      

      void main()
      {
        struct S *head,*pt;
        head=creat();
        pt=head;

        /*
        这是输出链表的方法
        */
        if(head!=NULL)
        {
        do
        {
          printf("%d ",pt->a);
          pt=pt->next;
        }while(pt!=NULL);
        }
      }

    至此,动态链表的创建和输出已经实现。

    怎么删除结点呢?实际上就是改变要删除的结点的前后的两个节点中地址的指向。
    怎么添加结点呢?实际上也是改变指向。
    使其做到有序?
    那么,我们就要进行一个大小比较的过程了。不过,要考虑在表首,表中,表尾三种情况。

    下面给出一个小栗子:

    要进行链表的结点添加,并顺序。
    #include<stdio.h>
    #include<stdlib.h>
    #include<malloc.h>
    #define LEN sizeof(struct Student)

    struct Student
    {
      long num;
      double score;
      struct Student *next;
    };
    int n=0;

    void print(struct Student *head)
    {
      struct Student *pt;
      pt=head;
      printf("there are %d records: ");
    if(head!=NULL)
    {
      do
      {
        printf("%ld,%5.1lf",pt->num,pt->score);
        pt=pt->next;
      }while(pt!=NULL);
    }

    }

    struct Student *creat()
    {
      struct Student *head,*p0,*p1;
      head=NULL;
      printf("*****creat list*****");
      printf("please input record(0,0 for exit) ");
      p0=(struct Student *)malloc(LEN);
      scanf("%ld,%lf",&p0->num,&p0->score);
    while(p0->num!=0)
    {
      head=insert(head,p0);
      p0=(struct Student *)malloc(LEN);
      scanf("%ld,%lf",&p0->num,&p0->score);
    }
    return (head);
    }

     

    struct Student *insert(struct Student *head,struct Student *stu)
    {
      struct Student *p0,*p1,*p2;
      p0=stu;
    if(head==NULL)
    {
      head=p0;
      p0->next=NULL;
    }
    else
    {
      p1=head;
      while(p0->num>p1->num&&p1->next!=NULL) //这是个遍历
    {
      p2=p1;
      p1=p1->next;
    }
      if(p0->num<=p1->num)
    {
      if(head==p1)
    {
      head=p0;
      p0->next=p1;
    }
    else
    {
      p2.next=p0;
      p0.next=p1;
    }
    }
    else
    {
      p1.next=p0;
      p0.next=NULL;
    }

    }

    n++;
    return(head);

    }  
      

     

     

     

     

     

    以上,请批评指正。
  • 相关阅读:
    ASP.NET MVC随想录——锋利的KATANA
    ASP.NET MVC随想录——漫谈OWIN
    Notepad++ 64位 插件管理
    C#实现按键精灵的'找图' '找色' '找字'的功能
    http://www.haolizi.net/example/view_2380.html
    C# 关于在原图中寻找子图片坐标的类
    WebBrowser控件默认使用IE9,IE10的方法
    Springboot---后台导出功能,easyExcel
    vue---EleElement UI 表格导出功能
    vue---提取公共方法
  • 原文地址:https://www.cnblogs.com/duye/p/6196571.html
Copyright © 2020-2023  润新知