C的資料結構

Download Report

Transcript C的資料結構

第九章
結構
結構的宣告

結構
 它是由許多不同 (或相同) 資料型態的變數所組成的
集合,通常利用結構標籤稱呼此集合
struct student {
char *name;
int score;
表示結構宣告至此為止,不可不寫
};
 struct 為保留字,表示結構的宣告開始
 結構項目需定義於大括號「{ }」內
 結尾需加上分號
 student 為結構標籤;name與score為結構之項目
定義結構變數

結構變數之定義
 結構宣告後,需定義結構變數後才能在程式中使用
 宣告結構並沒有配置記憶體,需等到定義結構變數
後才會配置記憶體
 您可以在宣告結構時順便定義結構變數s1和s2,如
struct student {
char *name;
int score;
} s1, s2;
定義結構變數
另一個宣告的方式是省略結構標籤,如下所
示:
struct {
char *name;
int score;
} s1, s2;
此方法有個缺點,在結構宣告後,此結構無
法被程式再利用來定義其它的結構變數,因
為沒有結構標籤可供程式識別
定義結構變數
 較好的結構定義方式
struct student {
char *name;
int score;
};
struct student s1, s2;
定義結構變數

定義結構時設定初值
&s1
struct student {
s1
char *name;
name
int score;
“Mary”
90
};
struct student s1 = {“Mary”, 90};
或
s1.name = “Mary”;
s1.score = 90;
其中「.」表示擷取某結構的資料項目,其語法為:
結構變數 . 結構項目
定義結構變數

當變數為指向結構的指標時,如
struct student *p;
p=&s1;
則其成員有兩種表示法:
(1) printf("%s\n", p->name);
printf("%d\n", p->score);
(2) printf("%s\n", (*p).name);
printf("%d\n", (*p).score);
&s1
p
s1
name
90
“Mary”
定義結構變數
/* File name: ex6-1a.c */
#include <stdio.h>
#include <stdlib.h>
int main()
{
struct student {
char *name;
int score;
};
struct student s = {"Mary", 90};
struct student *p = &s;
printf("s.name = %s\n", s.name);
printf("s.score = %d\n", s.score);
printf("p->name = %s\n", p->name);
printf("p->score = %d\n", p->score);
printf("(*p).name = %s\n", (*p).name);
printf("(*p).score = %d\n", (*p).score);
}
巢狀的結構變數

巢狀結構
 若結構中某一項目的資料型態又是一結構時,則稱
此結構為巢狀結構(nested structure),如
struct st_name {
char *firstname;
char *lastname;
};
struct student {
struct st_name name;
int score;
};
 則稱struct student為一巢狀結構,因為name為
struct st_name的資料型態
巢狀的結構變數

巢狀結構之項目存取
 若有一變數st
struct student st = {"Peter", "Wang", 95};
 則必須利用firstname 和 lastname
st.name.firstname
st.name.lastname
 運作之
巢狀的結構變數
/* File name: ex9-1b.c */
#include <stdio.h>
#include<stdlib.h>
&s1
int main()
{
st_name
s1
struct st_name {
firstname
name
char *firstname;
char *lastname;
lastname
score
};
struct student {
struct st_name name;
int score;
};
struct student s1 = {“Peter”, “Wang”, 95};
printf(“%s %s score is %d\n”, s1.name.firstname, s1.name.lastname,
s1.score)
}
以結構為參數的函數

以結構為參數的函數
 當呼叫一函數需傳遞結構時,只要將結構的位址傳
過去便可,而不是一個元素一個元素的傳,
 此種方式是屬於傳址呼叫(call by address),之後再
利用指標加以處理您欲達到的目的
 範例ex9-2a.c
自我參考結構

自我參考結構
 有一結構名為 node,其中若有項目指向 node 結構
指標
struct node {
char name[20];
int c-score;
struct node *next;
};
struct node *head = NULL, *tail, *this, *pre, *this;
 因為在struct node中,next為指向struct node的指
標
自我參考結構

自我參考結構之應用
 鏈結串列
 樹狀結構

以鏈結串列為例
struct node a, b; /* 表示目前有兩個結構變數a、b */
a.name = "John";
a
a.score = 90;
"John"
90
a.next = NULL;
b.name = "Mary";
b
b.score = 85;
“Mary"
85
b.next = NULL;
自我參考結構

動態記憶體配置
 當要建立一鏈結串列時,每一節點記憶體的配置以
malloc函數完成之
 malloc 函數之語法如下:
malloc(sizeof(struct node))
 calloc 函數之語法如下:
calloc(配置數目, bytes 數目)

釋放記憶體
 free (指標名稱);
自我參考結構

以malloc函數來配置記憶體
 假設有一指向struct node的指標ptr如下
struct node *ptr;
ptr = (struct node *) malloc(sizeof(struct node));
 此時ptr指標為指向剛剛被配置出來的結構
ptr
 範例ex9-3a.c
鏈結串列之建立

鏈結串列之建立
 鏈結串列之加入
 鏈結串列之刪除
 鏈結串列之顯示
 此處之加入與刪除之動作皆針對前端(當然您也可以
以尾端或特定節點為其對象)

範例ex9-4a.c
鏈結串列之建立
 加入的動作
new_node = (struct node *) malloc(sizeof(struct node));
if (head == NULL)
head = new_node;
else {
new_node->next = head;
head = new_node;
}
鏈結串列之建立

刪除的動作
if (head == NULL)
printf("No more node to delete…\n");
else {
del_node = head;
head = head->next;
free(del_node);
}
鏈結串列之建立

顯示的動作
if (head == NULL)
printf("No data to display…\n");
else {
current = head;
while (ptr != NULL) {
printf("%s\n", current ->name);
printf("%d\n\n", current ->score);
current = current->next;
}
}
第十章
再論結構
指標與結構的關係
/* File name: ex10-1a.c */
#include <stdio.h>
#include <stdlib.h>
int main()
{
struct student
{
char *name;
int score;
};
struct student st= {"Brian", 97};
struct student *ptr=&st;
指標與結構的關係
printf("++ptr->score = %d\n", ++ptr->score);
printf("ptr->name = %s\n", ptr->name);
printf("*ptr->name = %c\n", *ptr->name);
printf("*ptr->name++ = %c\n", *ptr->name++);
printf("*ptr->name = %c\n", *ptr->name);
printf("(*ptr->name)++ = %c\n", (*ptr->name)++);
printf("*ptr->name = %c\n", *ptr->name);
printf("*ptr++->name = %c\n", *ptr++->name);
}
指標與結構的關係

程式解說:
程式中的結構宣告與定義,如下圖所示:
ptr
&st
name
97
"Brian"
指標與結構的關係
(1) ++ptr->score
 由於->運算優先順序高於++,故上述等於
++(ptr->score),表示++乃作用於 (ptr>score) 之上,原先 score為 97,故將其
加 1,並更新之
ptr
&st
name
98
"Brian"
指標與結構的關係
(2) ptr->name
此敘述相當於(*ptr).name,以%s印出為
Brian
(3) *ptr->name
相當於*(ptr->name),得到的是B字元
(4) *ptr->name++
表示 *(ptr->name)++,由於*和++的運算
優先順序相同,並且其結合性是由右至左,
故此處的++乃作用於ptr->name,因此敘述
執行完畢後,得到B字元之後,將name的指
標移到 r 的位址上
指標與結構的關係
ptr
&st
name
98
"Brian"
(5) *ptr->name
驗證上述 name 的指標是否有指向下一個字
元,答案是 r
指標與結構的關係
(6) (*ptr->name)++
此處的++是針對(*ptr->name)而來,因
*ptr->name為 r,故將其加 1(即其下一個
字元),r 字元會變更為 s
ptr
&st
name
98
"Bsian"
指標與結構的關係
(7) *ptr->name
輸出結果為s
(8) *ptr++->name
由於此處的 ++ 乃作用於 ptr ,由於它是後
繼加,故先得到 s 值後,ptr 往下指向下一
個結構,若此時問 *ptr->name 為何?則答
案是 garbage,因為它已指向一個不是本題
所屬的記憶體了
指標與結構的關係
範例ex10-2 a.c
/* File name: ex10-2a.c */
#include <stdio.h>
#include <stdlib.h>
int main()
{
struct student {
char *name;
int score;
struct student *next;
};
struct student st[]= { {"John", 90, st+1},
{"Mary", 85, st+2},
{"Peter", 92, st} };
struct student *ptr=st;

指標與結構的關係
printf("st[0].name = %s\n", st[0].name);
printf("(*st).name = %s\n", (*st).name);
printf("ptr->name = %s\n", ptr->name);
printf("st[1].next->name = %s\n", st[1].next->name);
printf("st[1].next->score = %d\n", st[1].next->score);
printf("++(ptr->name) = %s\n", ++(ptr->name));
printf("++ptr->name = %s\n", ++ptr->name);
printf("ptr->next->name = %s\n", ptr->next->name);
printf("ptr->next->score = %d\n", ptr->next->score);
}
指標與結構的關係

程式解說:
程式中的結構宣告與定義,如下圖所示:
ptr
st
st[0]
name
90
next
"John"
st +1
st[1]
name
85
next
"Mary"
st + 2
st[2]
name
92
next
"Peter"
指標與結構的關係
(1) st [0].name以%s印出
 其值為John
(2) (*st).name以%s印出
 同(1)效果
(3) ptr->name
 因為 ptr 開始時設定在 st,故 ptr->name
等於 st[0].name 或(*st).name,皆為 John
指標與結構的關係
(4) st[1].next->name
其相當於 (st[1].next)->name,因為 . 和 > 具有相同的運算先順序,並且其結合性為
由左至右,由於 st[1].next 指向 st+2,故
st[1].next->name 為 Peter
(5) st[1].next->score
其值為92
(6) ++(ptr->name)以%s印出
其值為ohn,因為此處的++為前置加,故先
將name的指標指向下一個字元位址後,再
印出其值
指標與結構的關係
(7) ++ptr->name以%s印出
此敘述相當於++(ptr->name),同(6)其值
為hn
(8) ptr->next->name以%s印出
其結果為Mary,因為ptr->next指向st+1
(9) ptr->next->score
其值為85
指標與結構的應用

再論鏈結串列
加入與刪除之作用點不限定在哪,而要依據
題意加以搜尋
完整程式請參閱ex10-2b.c
指標與結構的應用

鏈結串列加入之片段程式
搜尋加入之位置
current = head;
while (current!=NULL && strcmp(new_node-> id,current->id) >= 0)
{
prev = current;
current = current → next;
}
指標與結構的應用
加入之動作
if (current == head) /* 加入在前端 */
{
new_node→next = head;
head = new_node;
}
else {
new_node→next = current;
prev→next = new_node;
}
指標與結構的應用

鏈結串列刪除之片段程式
搜尋欲刪除之節點
if (current = = head)
{
head = current->next;
free (current);
}
else {
prev->next = current->next;
free (current);
}
current指標指向欲刪除的節點,而prev則指
向欲刪除節點的前一節點