程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言基礎知識 >> C語言學習教程第七章-結構與聯合(5)

C語言學習教程第七章-結構與聯合(5)

編輯:C語言基礎知識

topoic=動態存儲分配

在數組一章中,曾介紹過數組的長度是預先定義好的, 在整個程序中固定不變。C語言中不允許動態數組類型。例如: int n;scanf("%d",&n);int a[n]; 用變量表示長度,想對數組的大小作動態說明, 這是錯誤的。但是在實際的編程中,往往會發生這種情況, 即所需的內存空間取決於實際輸入的數據,而無法預先確定。對於這種問題, 用數組的辦法很難解決。為了解決上述問題,C語言提供了一些內存管理函數,這些內存管理函數可以按需要動態地分配內存空間, 也可把不再使用的空間回收待用,為有效地利用內存資源提供了手段。 常用的內存管理函數有以下三個:

1.分配內存空間函數malloc
調用形式: (類型說明符*) malloc (size) 功能:在內存的動態存儲區中分配一塊長度為"size" 字節的連續區域。函數的返回值為該區域的首地址。 “類型說明符”表示把該區域用於何種數據類型。(類型說明符*)表示把返回值強制轉換為該類型指針。“size”是一個無符號數。例如: pc=(char *) malloc (100); 表示分配100個字節的內存空間,並強制轉換為字符數組類型, 函數的返回值為指向該字符數組的指針, 把該指針賦予指針變量pc。

2.分配內存空間函數 calloc
calloc 也用於分配內存空間。調用形式: (類型說明符*)calloc(n,size) 功能:在內存動態存儲區中分配n塊長度為“size”字節的連續區域。函數的返回值為該區域的首地址。(類型說明符*)用於強制類型轉換。calloc函數與malloc 函數的區別僅在於一次可以分配n塊區域。例如: ps=(struet stu*) calloc(2,sizeof (struct stu)); 其中的sizeof(struct stu)是求stu的結構長度。因此該語句的意思是:按stu的長度分配2塊連續區域,強制轉換為stu類型,並把其首地址賦予指針變量ps。

3.釋放內存空間函數free
調用形式: free(void*ptr); 功能:釋放ptr所指向的一塊內存空間,ptr 是一個任意類型的指針變量,它指向被釋放區域的首地址。被釋放區應是由malloc或calloc函數所分配的區域:[例7.9]分配一塊區域,輸入一個學生數據。
main()
{
struct stu
{
int num;
char *name;
char sex;
float score;
} *ps;
ps=(struct stu*)malloc(sizeof(struct stu));
ps->num=102;
ps->name="Zhang ping";
ps->sex='M';
ps->score=62.5;
printf("Number=%d\nName=%s\n",ps->num,ps->name);
printf("Sex=%c\nScore=%f\n",ps->sex,ps->score);
free(ps);
}
本例中,定義了結構stu,定義了stu類型指針變量ps。 然後分配一塊stu大內存區,並把首地址賦予ps,使ps指向該區域。再以ps為指向結構的指針變量對各成員賦值,並用printf 輸出各成員值。最後用free函數釋放ps指向的內存空間。 整個程序包含了申請內存空間、使用內存空間、釋放內存空間三個步驟, 實現存儲空間的動態分配。鏈表的概念在例7.9中采用了動態分配的辦法為一個結構分配內存空間。每一次分配一塊空間可用來存放一個學生的數據, 我們可稱之為一個結點。有多少個學生就應該申請分配多少塊內存空間, 也就是說要建立多少個結點。當然用結構數組也可以完成上述工作, 但如果預先不能准確把握學生人數,也就無法確定數組大小。 而且當學生留級、退學之後也不能把該元素占用的空間從數組中釋放出來。 用動態存儲的方法可以很好地解決這些問題。 有一個學生就分配一個結點,無須預先確定學生的准確人數,某學生退學, 可刪去該結點,並釋放該結點占用的存儲空間。從而節約了寶貴的內存資源。 另一方面,用數組的方法必須占用一塊連續的內存區域。 而使用動態分配時,每個結點之間可以是不連續的(結點內是連續的)。 結點之間的聯系可以用指針實現。 即在結點結構中定義一個成員項用來存放下一結點的首地址,這個用於存放地址的成員,常把它稱為指針域。可在第一個結點的指針域內存入第二個結點的首地址, 在第二個結點的指針域內又存放第三個結點的首地址, 如此串連下去直到最後一個結點。最後一個結點因無後續結點連接,其指針域可賦為0。這樣一種連接方式,在數據結構中稱為“鏈表”。圖7.3為鏈表的示意圖。

在圖7.3中,第0個結點稱為頭結點, 它存放有第一個結點的首地址,它沒有數據,只是一個指針變量。 以下的每個結點都分為兩個域,一個是數據域,存放各種實際的數據,如學號num,姓名name,性別sex和成績score等。另一個域為指針域, 存放下一結點的首地址。鏈表中的每一個結點都是同一種結構類型。例如, 一個存放學生學號和成績的結點應為以下結構:
struct stu
{ int num;
int score;
struct stu *next;
}
前兩個成員項組成數據域,後一個成員項next構成指針域, 它是一個指向stu類型結構的指針變量。鏈表的基本操作對鏈表的主要操作有以下幾種:
1.建立鏈表;
2.結構的查找與輸出;
3.插入一個結點;
4.刪除一個結點;
下面通過例題來說明這些操作。
[例7.10]建立一個三個結點的鏈表,存放學生數據。 為簡單起見, 我們假定學生數據結構中只有學號和年齡兩項。
可編寫一個建立鏈表的函數creat。程序如下:
#define NULL 0
#define TYPE struct stu
#define LEN sizeof (struct stu)
struct stu
{
int num;
int age;
struct stu *next;
};
TYPE *creat(int n)
{
struct stu *head,*pf,*pb;
int i;
for(i=0;i<n;i++)
{
pb=(TYPE*) malloc(LEN);
printf("input Number and Age\n");
scanf("%d%d",&pb->num,&pb->age);
if(i==0)
pf=head=pb;
else pf->next=pb;
pb->next=NULL;
pf=pb;
}
return(head);
}
在函數外首先用宏定義對三個符號常量作了定義。這裡用TYPE表示struct stu,用LEN表示sizeof(struct stu)主要的目的是為了在以下程序內減少書寫並使閱讀更加方便。結構stu定義為外部類型,程序中的各個函數均可使用該定義。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved