C語言入門教程 二十 堆和鍊錶

2021-06-08 02:48:42 字數 4484 閱讀 4239

我們經常在題目中有要求,輸入乙個整數,然後以這個整數作為陣列的元素個數,下面的程式**是錯誤的。

int n,array[n];

scanf(%d,&n);

在turbo c中,不允許出現動態陣列。那麼如果必須需要這樣時,就只能使用鍊錶了。

一、堆堆是一種動態儲存結構,實際上就是資料段中的自由儲存區,它是c語言中使用的一種名稱,常常用於動態資料的儲存分配。堆中存入一資料,總是以2位元組的整數倍進行分配,位址向增加方向變動。堆可以不斷進行分配直到沒有堆空間為止,也可以隨時進行釋放、再分配,不存在次序問題。

所謂動態陣列是指在程式執行期間確定其大小的,如常用到的動態陣列,它們是在程式執行過程中動態進行變化的,即在程式開始部分沒有說明大小,只有在程式執行期間用堆的分配函式為其分配儲存空間,分配的大小可根據需要而定,這些資料使用過後,可釋放它們占用的堆空間,並可進行再分配。

堆和棧在使用時相向生長,棧向上生長,即向小位址方向生長,而堆向下增長,即向大位址方向,其間剩餘部分是自由空間。使用過程中要防止增長過度而導致覆蓋。

一般的程式我們都是使用小記憶體模式,它的記憶體分配如下:

________________

| **段 |

|————————|

| 資料段 |

|————————|

| bss段 |

|————————|

| 堆 |

|----------------| 自由空間

|----------------|

| 棧 |

|————————|

| 遠堆 |

|----------------|

|________________| 自由空間

在堆和棧之間、以及遠堆位址的後面都是自由空間,總共是64k。

堆管理函式:

1.得到堆和棧之間的自由空間大小的函式

小資料記憶體模式:unsigned coreleft(void);

大資料記憶體模式:unsigned long coreleft(void);

對於遠堆,可以用farcoreleft()函式。

2.分配乙個堆空間函式

void malloc (unsigned size);

該函式將分配乙個大小為size位元組的堆空間,並返回乙個指向這個空間的指標。由於這個指標是void型的,因此當將它賦給其他型別的指標時,必須對該指標進行強制型別轉換。例如info是乙個結構型別指標,即:

struct addr *info;

將由malloc()函式返回的指標賦給info時,必須進行型別轉換:

info=(struct addr *)malloc (sizeof(record));

malloc()函式所分配的堆空間將不進行初始化。在呼叫malloc()函式時,若當時沒有可用的記憶體空間,該函式便返回乙個null指標。

3.分配乙個堆空間,其大小為能容納幾個元素,沒有元素長度為size的函式

void calloc(unsigned n,unsigned size);

該函式將分配乙個容量為n*size大小的堆空間,並用0初始化分配的空間。該函式將返回乙個指向分配空間的指標,沒有空間可用時,則返回乙個null指標。

4.重新分配堆空間函式

void *realloc(void *ptr,unsigned newsize);

該函式將對由ptr指向的堆空間重新分配,大小變為newsize。

5.釋放堆空間函式

void free(void *ptr);

下面舉乙個關於堆和棧的綜合例子:

void push(int);

int pop();

int *pi,*tos;

main()

tos=pi;

dowhile(v!=-1);

}void push(int i)

*pi=i;

}int pop()

pi--;

return *(pi+1);

}程式分配100位元組的堆空間,轉換成int型賦給pi,當pi為null時,表示沒有可用的空間了,則顯示allocation failure。輸入乙個整數,壓入棧中,當超過50時,則顯示stack overflow.當輸入0時,則把棧中的資料彈出。這個程式也演示了棧的後進先出的特點。

二、鍊錶

堆是用來儲存動態資料的。動態資料最典型的例子就是鍊錶。

形象的說:將若干個資料項按一定的原則前後鏈結起來,沒有資料項都有乙個指向下乙個資料的指標,則這些資料項靠指標鏈成乙個表,最後的乙個資料沒有指標(指標為null),這就是鍊錶。可以看出鍊錶放在儲存器中,並不一定象陣列一樣,連續存放,也可以分開存放。由於鏈的各節點均帶有指向下乙個節點的位址,因而要找到某個節點,必須要找到上乙個節點,如此類推,則可由第乙個節點出發找到目的點。鍊錶在資料庫建立和管理中用得比較普遍。

鍊錶中的每個節點都具有相同的結構型別,它們是由兩部分組成,即資料部分(它們包含一些有用的資訊),另一部分就是鏈的指標。下面就定義乙個通訊鏈節點的資料結構:

struct address

list_entry;

該結構中前五個成員是該節點的資訊部分,最後乙個成員是指向同乙個結構型別的指標。即next又指向乙個同樣結構型別的節點。

1.建立鍊錶

info=(struct address *)malloc(sizeof(list_entry));

當第乙個節點存入有info指出的記憶體區後,再執行該函式,便得到狹義個節點的儲存位址info,此時將該info賦給上乙個節點的next,並將該節點內容存入info指出的記憶體區,這樣兩個節點就鏈結起來了。此過程反覆多次,就可不斷的將節點加入鍊錶的尾端。

#include stdlib.h

#include alloc.h

#include stdio.h

#include string.h

struct address

list_entry;

void inputs(char *,char *,int);

void dls_store(struct address*);

main()

}void inputs(char *prompt,char *s,int count)

while(strlen(p)>count);

strcpy(s,p);

}void dls_store(struct address *in)

inputs()函式比較簡單,就不說明了。

dls_store()函式是將輸入的節點位址寫到上乙個節點的next指標項。其中定義的結構指標last是乙個靜態變數,初始值為null,這意味著在編譯時將為該變數分配乙個固定的儲存空間以存放其值。因初始值為null,這樣在第一次呼叫該函式時,由於它代表乙個空指標,因而把由malloc()分配的第乙個節點位址賦給它,使last指向該節點,第二次呼叫時,靜態變數last已指向第乙個節點位址。如此反覆呼叫,便建立起了n次呼叫產生的n個節點的鏈了(本題n=5)。

2.鏈資料的插入和刪除

對於乙個已排序好的鍊錶(假設是生序),現在想插入乙個資料進去,可能有三種情況:

(1).比首項資料還小,即插入的資料作為首項出現:

這種情況我們的處理方法是:把該資料作為第一項,指標指向原先的首項即可。設原先首項為top,待插入的資料為in,則:

in->next=top;

即可讓該資料作為鍊錶的頭。

(2).比最後一項大,即插入的資料作為最後一項出現:

這也很好辦,設原先最後一項為old,則:

old->next=in;

in->next=null;

(3).作為中間某一項出現:前面是old,後面是top,則:

old->next=in;

in->next=top;

如果想刪除乙個資料,也可能是出現在開頭,中間和結尾。

例如想刪除in這個資料,它原先的前面是old,後面是top,即原先的鍊錶是這樣:

old->next=in;

in->next=top;

現在刪除in,只需把old指向top即可:

old->next=top->next;

/*刪除節點函式*/

void delete(struct address *info,struct address *old)

free(info); /*釋放刪除節點占用空間*/}}

/*查詢鍊錶中是否有該資料*/

struct address *search(struct address *top,char *n)

return null; /*沒有找到*/

}/*鍊錶的輸出*/

void display(struct address *top)

}鍊錶問題比較複雜,但又是很重要的概念。上面說的輸入,查詢,刪除,插入等功能一定要理解,可以參考別的一些資料看看。

上面說的單鏈表,但是單鏈表有乙個缺點,就是無法反向操作,當某乙個鏈因破壞而斷裂,則整個鏈就被破壞而無法恢復。雙鏈表可以彌補這個缺點,所謂雙鏈表是指每個節點有兩個指標項,乙個指標指向其前面的節點,而另乙個指標指向後面的節點。關於雙鏈表的使用相對要複雜一些,這裡就不介紹了,可以找其他一些資料看看。

C語言入門教程2

要利用計算機處理問題,光學習語言的語法規則還不夠,最重要的是要學會針對各型別的問題,擬定出有效的解題方法和步驟。解題方法和步驟就是演算法。演算法 為了解決乙個問題而採取的有限步驟。計算機演算法 如何使計算機一步一步地工作的具體過程。利用計算機處理問題的步驟 1 設計好演算法 演算法設計 2 用計算機...

C語言入門教程 4 常量和變數

程式執行過程中其值不能發生改變的量叫做常量,其值能發生改變的量叫做變數。常量可以直接使用,而變數則必須先定義後才能使用,否則編譯器會報錯。在介紹常量和變數的命名規範之前,我們先了解一下什麼是識別符號和關鍵字。識別符號,用來識別符號號常量名 變數名 函式名 陣列名 檔名 類名 物件名等。簡單的將就是大...

C語言入門教程 二十二 檔案操作基本常識

由於程式中經常有大量對檔案的輸入輸出操作,它經常構成了程式的主要部分,因而c語言提供了很多輸入輸出的函式,它們分別用於兩種型別檔案輸入輸出系統 即由ansi標準定義的緩衝檔案 也稱標準檔案 流 輸入輸出 i o 系統 另一類是ansi標準中沒有定義的非緩衝檔案 也稱非標準檔案 流 輸入輸出 i o ...