鏈表的C語言實現(含動態記憶體分配)

來源:互聯網
上載者:User

鏈表的C語言實現(含動態記憶體分配)

上 鏈表的C語言實現之動態記憶體分配

 

一、為什麼用動態記憶體分配

  但我們未學習鏈表的時候,如果要儲存數量比較多的同類型或同結構的資料的時候,總是使用一個數組。比如說我們要儲存一個班級學生的某科分數,總是定義一個float型(存在0.5分)數組:

float score[30];

  但是,在使用數組的時候,總有一個問題困擾著我們:數組應該有多大?

  在很多的情況下,你並不能確定要使用多大的數組,比如上例,你可能並不知道該班級的學生的人數,那麼你就要把數組定義得足夠大。這樣,你的程式在運行時就申請了固定大小的你認為足夠大的記憶體空間。即使你知道該班級的學生數,但是如果因為某種特殊原因人數有增加或者減少,你又必須重新去修改程式,擴大數組的儲存範圍。這種分配固定大小的記憶體配置方法稱之為靜態記憶體配置。但是這種記憶體配置的方法存在比較嚴重的缺陷,特別是處理某些問題時:在大多數情況下會浪費大量的記憶體空間,在少數情況下,當你定義的數組不夠大時,可能引起下標越界錯誤,甚至導致嚴重後果。

  那麼有沒有其它的方法來解決這樣的外呢體呢?有,那就是動態記憶體分配。

  所謂動態記憶體分配就是指在程式執行的過程中動態地分配或者回收儲存空間的分配記憶體的方法。動態記憶體分配不象數組等靜態記憶體配置方法那樣需要預先分配儲存空間,而是由系統根據程式的需要即時分配,且分配的大小就是程式要求的大小。從以上動、靜態記憶體配置比較可以知道動態記憶體分配相對於景泰記憶體配置的特點:

  1、不需要預先分配儲存空間;

  2、分配的空間可以根據程式的需要擴大或縮小。

二、如何?動態記憶體分配及其管理

  要實現根據程式的需要動態分配儲存空間,就必須用到以下幾個函數

  1、malloc函數

  malloc函數的原型為:

void *malloc (unsigned int size)

  其作用是在記憶體的動態儲存裝置區中分配一個長度為size的連續空間。其參數是一個無符號整形數,傳回值是一個指向所分配的連續儲存域的起始地址的指標。還有一點必須注意的是,當函數未能成功分配儲存空間(如記憶體不足)就會返回一個NULL指標。所以在調用該函數時應該檢測傳回值是否為NULL並執行相應的操作。

下例是一個動態分配的程式:

#include "malloc.h"#include "stdlib.h"main(void){/*count是一個計數器,array是一個整型指標,也可以理解為指向一個整型數組的首地址*/int count;int *array;array=malloc(10 * sizeof(int));if(array==NULL){printf("Out of memory!\n");exit(1);}/*給數組賦值*/for(count=0;count<10;count++){array[count]=count;}/*列印數組元素*/for(count=0;count<10;count++){printf("%2d\n",array[count]);}}

  上例中動態分配了10個整型儲存地區,然後進行賦值並列印。例中if((array(int *) malloc(10*sizeof(int)))==NULL)語句可以分為以下幾步:

  1)分配10個整型的連續儲存空間,並返回一個指向其起始地址的整型指標

  2)把此整型指標地址賦給array

  3)檢測傳回值是否為NULL

  2、free函數

  由於記憶體地區總是有限的,不能不限制地分配下去,而且一個程式要盡量節省資源,所以當所分配的記憶體地區不用時,就要釋放它,以便其它的變數或者程式使用。這時我們就要用到free函數。

  其函數原型是:

void free(void *p)

  作用是釋放指標p所指向的記憶體區。

  其參數p必須是先前調用malloc函數或calloc函數(另一個動態分配儲存地區的函數)時返回的指標。給free函數傳遞其它的值很可能造成死機或其它災難性的後果。

  注意:這裡重要的是指標的值,而不是用來申請動態記憶體的指標本身。例:

int *p1,*p2;
p1=malloc(10*sizeof(int));
p2=p1;
……
free(p2) /*或者free(p2)*/

  malloc傳回值賦給p1,又把p1的值賦給p2,所以此時p1,p2都可作為free函數的參數。

  malloc函數是對儲存地區進行分配的。

  free函數是釋放已經不用的記憶體地區的。

  所以由這兩個函數就可以實現對記憶體地區進行動態分配並進行簡單的管理了。

 

中 鏈表的C語言實現之單鏈表的實現

一、單鏈表的建立

  有了動態記憶體分配的基礎,要實現鏈表就不難了。

  所謂鏈表,就是用一組任意的儲存單中繼存放區線性表元素的一種資料結構。鏈表又分為單鏈表、雙向鏈表和迴圈鏈表等。我們先講講單鏈表。所謂單鏈表,是指資料接點是單向排列的。一個單鏈表結點,其結構類型分為兩部分:

  1、資料域:用來儲存本身資料

  2、鏈域或稱為指標域:用來儲存下一個結點地址或者說指向其直接後繼的指標。

  例:

typedef struct node{ char name[20]; struct node *link;}stud; 

 

這樣就定義了一個單鏈表的結構,其中char name[20]是一個用來儲存姓名的字元型數組,指標*link是一個用來儲存其直接後繼的指標。

  定義好了鏈表的結構之後,只要在程式啟動並執行時候資料域中儲存適當的資料,如有後繼結點,則把鏈域指向其直接後繼,若沒有,則置為NULL。

  下面就來看一個建立帶表頭(若未說明,以下所指鏈表均帶表頭)的單鏈表的完整程式。

#include <stdio.h>#include <stdlib.h>#include <malloc.h> /*包含動態記憶體分配函數的標頭檔*/#define N 10 /*N為人數*/ typedef struct node{  char name[20];  struct node *link;}stud;stud * creat(int n) /*建立單鏈表的函數,形參n為人數*/{stud *p,*h,*s;/* *h儲存表頭結點的指標,*p指向當前結點的前一個結點,*s指向當前結點*/int i;/*計數器*/if((h=(stud *)malloc(sizeof(stud)))==NULL) /*分配空間並檢測*/{printf("不能分配記憶體空間!");exit(0);}h->name[0]='\0'; /*把表頭結點的資料域置空*/h->link=NULL; /*把表頭結點的鏈域置空*/p=h; /*p指向表頭結點*/for(i=0;i<n;i++){if((s=(stud *) malloc(sizeof(stud)))==NULL) /*分配新儲存空間並檢測*/{printf("不能分配記憶體空間!");exit(0);}p->link=s; /*把s的地址賦給p所指向的結點的鏈域,這樣就把p和s所指向的結點串連起來了*/printf("請輸入第%d個人的姓名",i+1);scanf("%s",s->name); /*在當前結點s的資料域中儲存姓名*/s->link=NULL;p=s;}return(h);}main(){  int number; /*儲存人數的變數*/  stud *head; /*head是儲存單鏈表的表頭結點地址的指標*/number=N;head=creat(number);/*把所建立的單鏈表表頭地址賦給head*/} 

  這樣就寫好了一個可以建立包含N個人姓名的單鏈表了。寫動態記憶體分配的程式應注意,請盡量對分配是否成功進行檢測。

 

下 鏈表的C語言實現之迴圈鏈表及雙向鏈表

 

一、迴圈鏈表

  迴圈鏈表是與單鏈表一樣,是一種鏈式的儲存結構,所不同的是,迴圈鏈表的最後一個結點的指標是指向該迴圈鏈表的第一個結點或者表頭結點,從而構成一個環形的鏈。

  迴圈鏈表的運算與單鏈表的運算基本一致。所不同的有以下幾點:

  1、在建立一個迴圈鏈表時,必須使其最後一個結點的指標指向表頭結點,而不是象單鏈表那樣置為NULL。此種情況還使用於在最後一個結點後插入一個新的結點。

  2、在判斷是否到表尾時,是判斷該結點鏈域的值是否是表頭結點,當鏈域值等於表頭指標時,說明已到表尾。而非象單鏈表那樣判斷鏈域值是否為NULL。

  二、雙向鏈表

  雙向鏈表其實是單鏈表的改進。

  當我們對單鏈表進行操作時,有時你要對某個結點的直接前驅進行操作時,又必須從表頭開始尋找。這是由單鏈表結點的結構所限制的。因為單鏈表每個結點只有一個儲存直接後繼結點地址的鏈域,那麼能不能定義一個既有儲存直接後繼結點地址的鏈域,又有儲存直接前驅結點地址的鏈域的這樣一個雙鏈域結點結構呢?這就是雙向鏈表。

  在雙向鏈表中,結點除含有資料域外,還有兩個鏈域,一個儲存直接後繼結點地址,一般稱之為右鏈域;一個儲存直接前驅結點地址,一般稱之為左鏈域。在c語言中雙向鏈表結點類型可以定義為:

typedef struct node{int data; /*資料域*/struct node *llink,*rlink; /*鏈域,*llink是左鏈域指標,*rlink是右鏈域指標*/}JD;

 

當然,也可以把一個雙向鏈表構建成一個雙向迴圈鏈表。

  雙向鏈表與單向鏈表一樣,也有三種基本運算:尋找、插入和刪除。

  雙向鏈表的基本運算:

  1、尋找

  假若我們要在一個帶表頭的雙向迴圈鏈表中尋找資料域為一特定值的某個結點時,我們同樣從表頭結點往後依次比較各結點資料域的值,若正是該特定值,則返回指向結點的指標,否則繼續往後查,直到表尾。

  下例就是應用雙向迴圈鏈表尋找演算法的一個程式。

#include <stdio.h>#include <malloc.h>#define N 10typedef struct node{ char name[20]; struct node *llink,*rlink;}stud;stud * creat(int n){ stud *p,*h,*s; int i; if((h=(stud *)malloc(sizeof(stud)))==NULL) {  printf("不能分配記憶體空間!");  exit(0); } h->name[0]=’\0’; h->llink=NULL; h->rlink=NULL; p=h; for(i=0;i<n;i++) {  if((s= (stud *) malloc(sizeof(stud)))==NULL)  {   printf("不能分配記憶體空間!");   exit(0);  }  p->rlink=s;  printf("請輸入第%d個人的姓名",i+1);  scanf("%s",s->name);  s->llink=p;  s->rlink=NULL;  p=s; } h->llink=s; p->rlink=h; return(h);}stud * search(stud *h,char *x){ stud *p; char *y; p=h->rlink; while(p!=h) {  y=p->name;  if(strcmp(y,x)==0)   return(p);  else p=p->rlink; } printf("沒有尋找到該資料!");}void print(stud *h){ int n; stud *p; p=h->rlink; printf("資料資訊為:\n"); while(p!=h) {  printf("%s ",&*(p->name));  p=p->rlink; } printf("\n");}main(){ int number; char studname[20]; stud *head,*searchpoint; number=N; clrscr(); head=creat(number); print(head); printf("請輸入你要尋找的人的姓名:"); scanf("%s",studname); searchpoint=search(head,studname); printf("你所要尋找的人的姓名是:%s",*&searchpoint->name);}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.