【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\n",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:\n");
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)\n");
  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);

}  
  

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM