亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

c語言中的結構(struct)和聯合(union)簡介

2019-11-17 05:13:57
字體:
來源:轉載
供稿:網友

  看到有朋友介紹union,我以前還沒有用過這個東西呢,也不懂,就去搜了點資料來看,也轉給大家,希望壇子里的給予改正或補充。謝謝!


聯 合(union)
1. 聯合說明和聯合變量定義
聯合也是一種新的數據類型, 它是一種非凡形式的變量。
聯合說明和聯合變量定義與結構十分相似。其形式為:
union 聯合名{
數據類型 成員名;
數據類型 成員名;
...
} 聯合變量名;
聯合表示幾個變量公用一個內存位置, 在不同的時間保存不同的數據類型 和不同長度的變量。
下例表示說明一個聯合a_bc:
union a_bc{
int i;
char mm;
};
再用已說明的聯合可定義聯合變量。
例如用上面說明的聯合定義一個名為lgc的聯合變量, 可寫成:
union a_bc lgc;
在聯合變量lgc中, 整型量i和字符mm公用同一內存位置。
當一個聯合被說明時, 編譯程序自動地產生一個變量, 其長度為聯合中最大的變量長度。
聯合訪問其成員的方法與結構相同。同樣聯合變量也可以定義成數組或指針,但定義為指針時, 也要用"->"符號, 此時聯合訪問成員可表示成:
聯合名->成員名
另外, 聯合既可以出現在結構內, 它的成員也可以是結構。
例如:
strUCt{
int age;
char *addr;
union{
int i;
char *ch;
}x;
}y[10];
若要訪問結構變量y[1]中聯合x的成員i, 可以寫成:
y[1].x.i;
若要訪問結構變量y[2]中聯合x的字符串指針ch的第一個字符可寫成:
*y[2].x.ch;
若寫成"y[2].x.*ch;"是錯誤的。

2. 結構和聯合的區別
結構和聯合有下列區別:
1. 結構和聯合都是由多個不同的數據類型成員組成, 但在任何同一時刻, 聯合轉只存放了一個被選中的成員, 而結構的所有成員都存在。
2. 對于聯合的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對于結構的不同成員賦值是互不影響的。
下面舉一個例了來加對深聯合的理解。
例4:
main()
{
union{ /*定義一個聯合*/
int i;
struct{ /*在聯合中定義一個結構*/
char first;
char second;
}half;
}number;

number.i=0x4241; /*聯合成員賦值*/
number.half.first='a'; /*聯合中結構成員賦值*/
number.half.second='b';
printf("%x/n", number.i);
getch();
}
輸出結果為:
AB
6261
從上例結果可以看出: 當給i賦值后, 其低八位也就是first和second的值;當給first和second賦字符后, 這兩個字符的ASCII碼也將作為i 的低八位和高八位。

結構類型定義和結構變量說明

  在實際問題中,一組數據往往具有不同的數據類型。例如,在學生登記表中,姓名應為字符型;學號可為整型或字符型; 年齡應為整型;性別應為字符型;成績可為整型或實型。 顯然不能用一個數組來存放這一組數據。 因為數組中各元素的類型和長度都必須一致,以便于編譯系統處理。為了解決這個問題, C語言中給出了另一種構造數據類型----“結構”。它相當于其它高級語言中的記錄。

  “結構”是一種構造類型,它是由若干“成員”組成的。每一個成員可以是一個基本數據類型或者又是一個構造類型。結構既是一種“構造”而成的數據類型,那么在說明和使用之前必須先定義它,也就是構造它。如同在說明和調用函數之前要先定義函數一樣。

一、結構的定義

定義一個結構的一般形式為:
struct 結構名
{
成員表列
};
成員表由若干個成員組成, 每個成員都是該結構的一個組成部分。對每個成員也必須作類型說明,其形式為:
類型說明符 成員名;
成員名的命名應符合標識符的書寫規定。例如:
struct stu
{
int num;
char name[20];
char sex;
float score;
};

  在這個結構定義中,結構名為stu,該結構由4個成員組成。第一個成員為num,整型變量;第二個成員為name,字符數組;第三個成員為 sex,字符變量;第四個成員為score,實型變量。 應注重在括號后的分號是不可少的。結構定義之后,即可進行變量說明。 凡說明為結構stu的變量都由上述4個成員組成。由此可見, 結構是一種復雜的數據類型,是數目固定,類型不同的若干有序變量的集合。

二、結構類型變量的說明

說明結構變量有以下三種方法。以上面定義的stu為例來加以說明。
1. 先定義結構,再說明結構變量。如:
struct stu
{
int num;
char name[20];
char sex;
float score;
};
struct stu boy1,boy2;
說明了兩個變量boy1和boy2為stu結構類型。也可以用宏定義使一個符號常量來表示一個結構類型,例如:
#define STU struct stu
STU
{
int num;
char name[20];
char sex;
float score;
};
STU boy1,boy2;

2. 在定義結構類型的同時說明結構變量。例如:
struct stu
{
int num;
char name[20];
char sex;
float score;
}boy1,boy2;

3. 直接說明結構變量。例如:
struct
{
int num;
char name[20];
char sex;
float score;
}boy1,boy2;

  第三種方法與第二種方法的區別在于第三種方法中省去了結構名,而直接給出結構變量。三種方法中說明的boy1,boy2變量都具有圖7.1所示的結構。說明了boy1,boy2變量為stu類型后,即可向這兩個變量中的各個成員賦值。在上述stu結構定義中,所有的成員都是基本數據類型或數組類型。成員也可以又是一個結構, 即構成了嵌套的結構。例如,圖7.2給出了另一個數據結構。 按圖7.2可給出以下結構定義:
struct date{
int month;
int day;
int year;
}
struct{
int num;
char name[20];
char sex;
struct date birthday;
float score;
}boy1,boy2;
  首先定義一個結構date,由month(月)、day(日)、year(年)三個成員組成。在定義并說明變量 boy1 和 boy2 時, 其中的成員birthday被說明為data結構類型。成員名可與程序中其它變量同名,互不干擾。結構變量成員的表示方法在程序中使用結構變量時, 往往不把它作為一個整體來使用。

  在ANSI C中除了答應具有相同類型的結構變量相互賦值以外, 一般對結構變量的使用,包括賦值、輸入、輸出、 運算等都是通過結構變量的成員來實現的。

  表示結構變量成員的一般形式是: 結構變量名.成員名 例如:boy1.num 即第一個人的學號 boy2.sex 即第二個人的性別 假如成員本身又是一個結構則必須逐級找到最低級的成員才能使用。例如:boy1.birthday.month 即第一個人出生的月份成員可以在程序中單獨使用,與普通變量完全相同。

結構變量的賦值

前面已經介紹,結構變量的賦值就是給各成員賦值??捎幂斎胝Z句或賦值語句來完成。
[例7.1]給結構變量賦值并輸出其值。
[code:1:8d8ee8c82c]
main(){
struct stu
{
int num;
char *name;
char sex;
float score;
} boy1,boy2;

boy1.num=102;
boy1.name="Zhang ping";
printf("input sex and score/n");
scanf("%c %f",&boy1.sex,&boy1.score);
boy2=boy1;
printf("Number=%d/nName=%s/n",boy2.num,boy2.name);
printf("Sex=%c/nScore=%f/n",boy2.sex,boy2.score);
}

[/code:1:8d8ee8c82c]

  本程序中用賦值語句給num和name兩個成員賦值,name是一個字符串指針變量。用scanf函數動態地輸入sex和score成員值, 然后把boy1的所有成員的值整體賦予boy2。最后分別輸出boy2 的各個成員值。本例表示了結構變量的賦值、輸入和輸出的方法。

結構變量的初始化
  假如結構變量是全局變量或為靜態變量, 則可對它作初始化賦值。對局部或自動結構變量不能作初始化賦值。
[例7.2]外部結構變量初始化。
[code:1:8d8ee8c82c]
struct stu /*定義結構*/
{
int num;
char *name;

char sex;
float score;
} boy2,boy1={102,"Zhang ping",'M',78.5};

main()
{
boy2=boy1;
printf("Number=%d/nName=%s/n",boy2.num,boy2.name);
printf("Sex=%c/nScore=%f/n",boy2.sex,boy2.score);
}

[/code:1:8d8ee8c82c]
本例中,boy2,boy1均被定義為外部結構變量,并對boy1作了初始化賦值。在main函數中,把boy1的值整體賦予boy2, 然后用兩個printf語句輸出boy2各成員的值。

[例7.3]靜態結構變量初始化。
[code:1:8d8ee8c82c]
main()
{
static struct stu /*定義靜態結構變量*/
{
int num;
char *name;
char sex;
float score;
}boy2,boy1={102,"Zhang ping",'M',78.5};
boy2=boy1;
printf("Number=%d/nName=%s/n",boy2.num,boy2.name);
printf("Sex=%c/nScore=%f/n",boy2.sex,boy2.score);
}
[/code:1:8d8ee8c82c]
  本例是把boy1,boy2都定義為靜態局部的結構變量, 同樣可以作初始化賦值。

結構數組

數組的元素也可以是結構類型的。 因此可以構成結構型數組。結構數組的每一個元素都是具有相同結構類型的下標結構變量。 在實際應用中,經常用結構數組來表示具有相同數據結構的一個群體。如一個班的學生檔案,一個車間職工的工資表等。
結構數組的定義方法和結構變量相似,只需說明它為數組類型即可。例如:
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5];
定義了一個結構數組boy1,共有5個元素,boy[0]~boy[4]。每個數組元素都具有struct stu的結構形式。 對外部結構數組或靜態結構數組可以作初始化賦值,例如:
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping","M",45},
{102,"Zhang ping","M",62.5},
{103,"He fang","F",92.5},
{104,"Cheng ling","F",87},
{105,"Wang ming","M",58};
}
當對全部元素作初始化賦值時,也可不給出數組長度。
[例7.4]計算學生的平均成績和不及格的人數。
[code:1:8d8ee8c82c]
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
int i,c=0;
float ave,s=0;
for(i=0;i<5;i++)
{
s+=boy[i].score;
if(boy[i].score<60) c+=1;
}
printf("s=%f/n",s);
ave=s/5;
printf("average=%f/ncount=%d/n",ave,c);
}
[/code:1:8d8ee8c82c]
本例程序中定義了一個外部結構數組boy,共5個元素, 并作了初始化賦值。在main函數中用for語句逐個累加各元素的score 成員值存于s之中,如score的值小于60(不及格)即
計數器C加1, 循環完畢后計算平均成績,并輸出全班總分,平均分及不及格人數。

[例7.5]建立同學通訊錄
[code:1:8d8ee8c82c]
#include"stdio.h"
#define NUM 3
struct mem
{
char name[20];
char phone[10];
};
main()
{
struct mem man[NUM];
int i;
for(i=0;i<NUM;i++)
{
printf("input name:/n");
gets(man[i].name);
printf("input phone:/n");
gets(man[i].phone);
}
printf("name/t/t/tphone/n/n");

for(i=0;i<NUM;i++)
printf("%s/t/t/t%s/n",man[i].name,man[i].phone);
}
[/code:1:8d8ee8c82c]
  本程序中定義了一個結構mem,它有兩個成員name和phone 用來表示姓名和電話號碼。在主函數中定義man為具有mem 類型的結構數組。在for語句中,用gets函數分別輸入各個元素中兩個成員的值。然后又在for語句中用printf語句輸出各元素中兩個成員值。

結構指針變量

結構指針變量的說明和使用一個指針變量當用來指向一個結構變量時, 稱之為結構指針變量。
結構指針變量中的值是所指向的結構變量的首地址。 通過結構指針即可訪問該結構變量, 這與數組指針和函數指針的情況是相同的。結構指針變量說明的一般形式為:
struct 結構名*結構指針變量名
例如,在前面的例7.1中定義了stu這個結構, 如要說明一個指向stu的指針變量pstu,可寫為:
struct stu *pstu;

  當然也可在定義stu結構時同時說明pstu。與前面討論的各類指針變量相同,結構指針變量也必須要先賦值后才能使用。賦值是把結構變量的首地址賦予該指針變量, 不能把結構名賦予該指針變量。假如boy是被說明為stu類型的結構變量,則: pstu=&boy是正確的,而: pstu=&stu是錯誤的。

  結構名和結構變量是兩個不同的概念,不能混淆。 結構名只能表示一個結構形式,編譯系統并不對它分配內存空間。 只有當某變量被說明為這種類型的結構時,才對該變量分配存儲空間。 因此上面&stu這種寫法是錯誤的,不可能去取一個結構名的首地址。 有了結構指針變量,就能更方便地訪問結構變量的各個成員。

其訪問的一般形式為: (*結構指針變量).成員名 或為:
結構指針變量->成員名
例如: (*pstu).num或者: pstu->num
應該注重(*pstu)兩側的括號不可少, 因為成員符“.”的優先級高于“*”。如去掉括號寫作*pstu.num則等效于*(pstu.num),這樣,意義就完全不對了。 下面通過例子來說明結構指針變量的具體說明和使用方法。
[例7.6]
[code:1:8d8ee8c82c]
struct stu
{
int num;
char *name;
char sex;
float score;
} boy1={102,"Zhang ping",'M',78.5},*pstu;
main()
{
pstu=&boy1;
printf("Number=%d/nName=%s/n",boy1.num,boy1.name);
printf("Sex=%c/nScore=%f/n/n",boy1.sex,boy1.score);
printf("Number=%d/nName=%s/n",(*pstu).num,(*pstu).name);
printf("Sex=%c/nScore=%f/n/n",(*pstu).sex,(*pstu).score);
printf("Number=%d/nName=%s/n",pstu->num,pstu->name);
printf("Sex=%c/nScore=%f/n/n",pstu->sex,pstu->score);
}
[/code:1:8d8ee8c82c]
  本例程序定義了一個結構stu,定義了stu類型結構變量boy1 并作了初始化賦值,還定義了一個指向stu類型結構的指針變量pstu。在main函數中,pstu被賦予boy1的地址,因此pstu指向boy1 。然后在printf語句內用三種形式輸出boy1的各個成員值。 從運行結果可以看出:
結構變量.成員名
(*結構指針變量).成員名
結構指針變量->成員名

  這三種用于表示結構成員的形式是完全等效的。結構數組指針變量結構指針變量可以指向一個結構數組, 這時結構指針變量的值是整個結構數組的首地址。 結構指針變量也可指向結構數組的一個元素,這時結構指針變量的值是該結構數組元素的首地址。設ps為指向結構數組的指針變量,則ps也指向該結構數組的0號元素,ps+1指向1號元素,ps+i則指向i號元素。 這與普通數組的情況是一致的。
[例7.7]用指針變量輸出結構數組。
[code:1:8d8ee8c82c]
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Zhou ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"Liou fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
struct stu *ps;
printf("No/tName/t/t/tSex/tScore/t/n");
for(ps=boy;ps<boy+5;ps++)
printf("%d/t%s/t/t%c/t%f/t/n",ps->num,ps->name,ps->sex,ps->score);
}
[/code:1:8d8ee8c82c]
  在程序中,定義了stu結構類型的外部數組boy 并作了初始化賦值。在main函數內定義ps為指向stu類型的指針。在循環語句for的表達式1中,ps被賦予boy的首地址,然后循環5次,輸出boy數組中各成員值。 應該注重的是, 一個結構指針變量雖然可以用來訪問結構變量或結構數組元素的成員,但是,不能使它指向一個成員。 也就是說不答應取一個成員的地址來賦予它。因此,下面的賦值是錯誤的。 ps=&boy[1]. sex;而只能是:ps=boy;(賦予數組首地址)

或者是:
ps=&boy[0];(賦予0號元素首地址)

結構指針變量作函數參數

  在ANSI C標準中答應用結構變量作函數參數進行整體傳送。 但是這種傳送要將全部成員逐個傳送, 非凡是成員為數組時將會使傳送的時間和空間開銷很大,嚴重地降低了程序的效率。 因此最好的辦法就是使用指針,即用指針變量作函數參數進行傳送。 這時由實參傳向形參的只是地址,從而減少了時間和空間的開銷。
[例7.8]題目與例7.4相同,計算一組學生的平均成績和不及格人數。
用結構指針變量作函數參數編程。
[code:1:8d8ee8c82c]
struct stu
{
int num;
char *name;
char sex;
float score;
}boy[5]={
{101,"Li ping",'M',45},
{102,"Zhang ping",'M',62.5},
{103,"He fang",'F',92.5},
{104,"Cheng ling",'F',87},
{105,"Wang ming",'M',58},
};
main()
{
struct stu *ps;
void ave(struct stu *ps);
ps=boy;
ave(ps);
}
void ave(struct stu *ps)
{
int c=0,i;
float ave,s=0;
for(i=0;i<5;i++,ps++)
{
s+=ps->score;
if(ps->score<60) c+=1;
}
printf("s=%f/n",s);
ave=s/5;
printf("average=%f/ncount=%d/n",ave,c);
}
[/code:1:8d8ee8c82c]
  本程序中定義了函數ave,其形參為結構指針變量ps。boy 被定義為外部結構數組,因此在整個源程序中有效。在main 函數中定義說明了結構指針變量ps,并把boy的首地址賦予它,使ps指向boy 數組。然后以ps作實參調用函數ave。在函數ave 中完成計算平均成績和統計不及格人數的工作并輸出結果。與例7.4程序相比,由于本程序全部采用指針變量作運算和處理,故速度更快,程序效率更高。.

topoic=動態存儲分配

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

1.分配內存空間函數malloc
調用形式: (類型說明符*) malloc (size) 功能:在內存的動態存儲區中分配一塊長度為"size" 字節的連續區域?姆禱刂滴們虻氖椎刂貳?“類型說明符”表示把該區域用于何種數據類型。(類型說明符*)表示把返回值強制轉換為該類型指針?!皊ize”是一個無符號數。例如: 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]分配一塊區域,輸入一個學生數據。
[code:1:8d8ee8c82c]
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);
}
[/code:1:8d8ee8c82c]
  本例中,定義了結構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。程序如下:
[code:1:8d8ee8c82c]
#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);
}
[/code:1:8d8ee8c82c]
  在函數外首先用宏定義對三個符號常量作了定義。這里用TYPE表示struct stu,用LEN表示sizeof(struct stu)主要的目的是為了在以下程序內減少書寫并使閱讀更加方便。結構stu定義為外部類型,程序中的各個函數均可使用該定義。

  creat函數用于建立一個有n個結點的鏈表,它是一個指針函數,它返回的指針指向stu結構。在creat函數內定義了三個stu結構的指針變量。head為頭指針,pf 為指向兩相鄰結點的前一結點的指針變量。pb為后一結點的指針變量。在for語句內,用malloc函數建立長度與 stu長度相等的空間作為一結點,首地址賦予pb。然后輸入結點數據。假如當前結點為第一結點(i==0),則把pb值 (該結點指針)賦予head和 pf。如非第一結點,則把pb值賦予pf 所指結點的指針域成員next。而pb所指結點為當前的最后結點,其指針域賦NULL。 再把pb值賦予pf以作下一次循環預備。
  creat函數的形參n,表示所建鏈表的結點數,作為for語句的循環次數。圖7.4表示了creat函數的執行過程。

[例7.11]寫一個函數,在鏈表中按學號查找該結點。
[code:1:8d8ee8c82c]
TYPE * search (TYPE *head,int n)
{
TYPE *p;
int i;
p=head;
while (p->num!=n && p->next!=NULL)
p=p->next; /* 不是要找的結點后移一步*/
if (p->num==n) return (p);
if (p->num!=n&& p->next==NULL)
printf ("Node %d has not been found!/n",n);
}
[/code:1:8d8ee8c82c]
  本函數中使用的符號常量TYPE與例7.10的宏定義相同,等于struct stu?辛礁魴尾?head是指向鏈表的指針變量,n為要查找的學號。進入while語句,逐個檢查結點的num成員是否等于n,假如不等于n且指針域不等于NULL(不是最后結點)則后移一個結點,繼續循環。如找到該結點則返回結點指針。 如循環結束仍未找到該結點則輸出“未找到”的提示信息。


[例7.12]寫一個函數,刪除鏈表中的指定結點。刪除一個結點有兩種情況:
1. 被刪除結點是第一個結點。這種情況只需使head指向第二個結點即可。即head=pb->next。其過程如圖7.5所示。
2. 被刪結點不是第一個結點,這種情況使被刪結點的前一結點指向被刪結點的后一結點即可。即pf->next=pb->next。其過程如圖7.6所示。
函數編程如下:
[code:1:8d8ee8c82c]
TYPE * delete(TYPE * head,int num)
{
TYPE *pf,*pb;
if(head==NULL) /*如為空表, 輸出提示信息*/
{
printf("/nempty list!/n");
goto end;
}
pb=head;
while (pb->num!=num && pb->next!=NULL)
/*當不是要刪除的結點,而且也不是最后一個結點時,繼續循環*/
{pf=pb;pb=pb->next;}/*pf指向當前結點,pb指向下一結點*/
if(pb->num==num)
{
if(pb==head)
head=pb->next;
/*如找到被刪結點,且為第一結點,則使head指向第二個結點, 否則使pf所指結點的指針指向下一結點*/
else
pf->next=pb->next;
free(pb);
printf("The node is deleted/n");
}
else
printf("The node not been foud!/n");

end:
return head;
}
[/code:1:8d8ee8c82c]
  函數有兩個形參,head為指向鏈表第一結點的指針變量,num刪結點的學號。 首先判定鏈表是否為空,為空則不可能有被刪結點。若不為空, 則使pb指針指向鏈表的第一個結點。進入while語句后逐個查找被刪結點。找到被刪結點之后再看是否為第一結點,若是則使head指向第二結點(即把第一結點從鏈中刪去),否則使被刪結點的前一結點(pf所指)指向被刪結點的后一結點(被刪結點的指針域所指)。如若循環結束未找到要刪的結點, 則輸出 “末找到”的提示信息。最后返回head值。

[例7.13]寫一個函數,在鏈表中指定位置插入一個結點。在一個鏈表的指定位置插入結點, 要求鏈表本身必須是已按某種規律排好序的。例如,在學生數據鏈表中, 要求學號順序插入一個結點。設被插結點的指針為pi。 可在三種不同情況下插入。
1. 原表是空表,只需使head指向被插結點即可。見圖7.7(a)
2. 被插結點值最小,應插入第一結點之前。這種情況下使head指向被插結點,被插結點的指針域指向原來的第一結點則可。即:pi->next=pb;
head=pi; 見圖7.7(b)
3. 在其它位置插入,見圖7.7(c)。這種情況下,使插入位置的前一結點的指針域指向被插結點,使被插結點的指針域指向插入位置的后一結點。即為:pi->next=pb;pf->next=pi;
4. 在表末插入,見圖7.7(d)。這種情況下使原表末結點指針域指向被插結點,被插結點指針域置為NULL。即:
[code:1:8d8ee8c82c]
pb->next=pi;
pi->next=NULL; TYPE * insert(TYPE * head,TYPE *pi)
{
TYPE *pf,*pb;
pb=head;
if(head==NULL) /*空表插入*/
{
head=pi;
pi->next=NULL;
}
else
{
while((pi->num>pb->num)&&(pb->next!=NULL))
{
pf=pb;
pb=pb->next;
}/*找插入位置*/
if(pi->num<=pb->num)
{
if(head==pb)
head=pi;/*在第一結點之前插入*/
else
pf->next=pi;/*在其它位置插入*/
pi->next=pb;
}
else
{
pb->next=pi;
pi->next=NULL;
} /*在表末插入*/
}
return head;
}
[/code:1:8d8ee8c82c]
  本函數有兩個形參均為指針變量,head指向鏈表,pi 指向被插結點?惺紫擾卸狹幢硎欠裎?為空則使head指向被插結點。表若不空,則用while語句循環查找插入位置。找到之后再判定是否在第一結點之前插入,若是則使head 指向被插結點被插結點指針域指向原第一結點,否則在其它位置插入, 若插入的結點大于表中所有結點,則在表末插入。本函數返回一個指針, 是鏈表的頭指針。 當插入的位置在第一個結點之前時, 插入的新結點成為鏈表的第一個結點,因此head的值也有了改變, 故需要把這個指針返回主調函數。
[例7.14]將以上建立鏈表,刪除結點,插入結點的函數組織在一起,再建一個輸出全部結點的函數,然后用main函數調用它們。

[code:1:8d8ee8c82c]
#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 * delete(TYPE * head,int num)
{
TYPE *pf,*pb;
if(head==NULL)
{
printf("/nempty list!/n");
goto end;
}
pb=head;
while (pb->num!=num && pb->next!=NULL)
{pf=pb;pb=pb->next;}
if(pb->num==num)
{
if(pb==head) head=pb->next;
else pf->next=pb->next;
printf("The node is deleted/n");
}
else
free(pb);
printf("The node not been found!/n");

end:
return head;
}

TYPE * insert(TYPE * head,TYPE * pi)
{
TYPE *pb ,*pf;
pb=head;
if(head==NULL)
{
head=pi;
pi->next=NULL;
}
else
{
while((pi->num>pb->num)&&(pb->next!=NULL))
{ pf=pb;
pb=pb->next; }
if(pi->num<=pb->num)
{
if(head==pb) head=pi;
else pf->next=pi;
pi->next=pb;
}
else
{
pb->next=pi;
pi->next=NULL;
}
}
return head;
}

void print(TYPE * head)
{
printf("Number/t/tAge/n");
while(head!=NULL)
{
printf("%d/t/t%d/n",head->num,head->age);
head=head->next;
}
}
main()
{
TYPE * head,*pnum;
int n,num;
printf("input number of node: ");
scanf("%d",&n);
head=creat(n);
print(head);
printf("Input the deleted number: ");
scanf("%d",&num);
head=delete(head,num);
print(head);
printf("Input the inserted number and age: ");
pnum=(TYPE *)malloc(LEN);
scanf("%d%d",&pnum->num,&pnum->age);
head=insert(head,pnum);
print(head);
}
[/code:1:8d8ee8c82c]
  本例中,print函數用于輸出鏈表中各個結點數據域值?男尾蝖ead的初值指向鏈表第一個結點。在while語句中,輸出結點值后, head值被改變,指向下一結點。若保留頭指針head, 則應另設一個指針變量,把head值賦予它,再用它來替代head。在main函數中,n為建立結點的數目, num為待刪結點的數據域值;head為指向鏈表的頭指針,pnum為指向待插結點的指針。 main函數中各行的意義是:

第六行輸入所建鏈表的結點數;
第七行調creat函數建立鏈表并把頭指針返回給head;
第八行調print函數輸出鏈表;
第十行輸入待刪結點的學號;
第十一行調delete函數刪除一個結點;
第十二行調print函數輸出鏈表;
第十四行調malloc函數分配一個結點的內存空間, 并把其地址賦予pnum;
第十五行輸入待插入結點的數據域值;
第十六行調insert函數插入pnum所指的結點;
第十七行再次調print函數輸出鏈表。

  從運行結果看,首先建立起3個結點的鏈表,并輸出其值;再刪103號結點,只剩下105,108號結點;又輸入106號結點數據, 插入后鏈表中的結點為105,106,108。聯合“聯合”也是一種構造類型的數據結構。 在一個“聯合”內可以定義多種不同的數據類型, 一個被說明為該“聯合”類型的變量中,答應裝入該“聯合”所定義的任何一種數據。 這在前面的各種數據類型中都是辦不到的。例如, 定義為整型的變量只能裝入整型數據,定義為實型的變量只能賦予實型數據。

  在實際問題中有很多這樣的例子。 例如在學校的教師和學生中填寫以下表格: 姓 名 年 齡 職 業 單位 “職業”一項可分為“教師”和 “學生”兩類。 對“單位”一項學生應填入班級編號,教師應填入某系某教研室。 班級可用整型量表示,教研室只能用字符類型。 要求把這兩種類型不同的數據都填入“單位”這個變量中, 就必須把“單位”定義為包含整型和字符型數組這兩種類型的“聯合”。

  “聯合”與“結構”有一些相似之處。但兩者有本質上的不同。在結構中各成員有各自的內存空間, 一個結構變量的總長度是各成員長度之和。而在 “聯合”中,各成員共享一段內存空間, 一個聯合變量的長度等于各成員中最長的長度。應該說明的是, 這里所謂的共享不是指把多個成員同時裝入一個聯合變量內, 而是指該聯合變量可被賦予任一成員值,但每次只能賦一種值, 賦入新值則沖去舊值。如前面介紹的“單位”變量, 如定義為一個可裝入“班級”或 “教研室”的聯合后,就答應賦予整型值(班級)或字符串(教研室)。要么賦予整型值,要么賦予字符串,不能把兩者同時賦予它。聯合類型的定義和聯合變量的說明一個聯合類型必須經過定義之后, 才能把變量說明為該聯合類型。

一、聯合的定義

定義一個聯合類型的一般形式為:
union 聯合名
{
成員表
};
成員表中含有若干成員,成員的一般形式為: 類型說明符 成員名 成員名的命名應符合標識符的規定。
例如:
union perdata
{
int class;
char Office[10];
};
  定義了一個名為perdata的聯合類型,它含有兩個成員,一個為整型,成員名為class;另一個為字符數組,數組名為office。聯合定義之后,即可進行聯合變量說明,被說明為perdata類型的變量,可以存放整型量class或存放字符數組office。

二、聯合變量的說明

  聯合變量的說明和結構變量的說明方式相同, 也有三種形式。即先定義,再說明;定義同時說明和直接說明。以perdata類型為例,說明如下:
union perdata
{
int class;
char officae[10];
};
union perdata a,b; /*說明a,b為perdata類型*/
或者可同時說明為:
union perdata
{
int class;
char office[10];
}a,b;
或直接說明為:
union
{
int class;
char office[10];
}a,b
經說明后的a,b變量均為perdata類型。 它們的內存分配示意圖如圖7—8所示。a,b變量的長度應等于 perdata 的成員中最長的長度, 即等于
office數組的長度,共10個字節。從圖中可見,a,b變量如賦予整型值時,只使用了2個字節,而賦予字符數組時,可用10個字節。

聯合變量的賦值和使用

  對聯合變量的賦值,使用都只能是對變量的成員進行。 聯合變量的成員表示為: 聯合變量名.成員名 例如,a被說明為perdata類型的變量之后,可使用 a.class a.office 不答應只用聯合變量名作賦值或其它操作。 也不答應對聯合變量作初始化賦值,賦值只能在程序中進行? 掛僨康魎得韉氖?一個聯合變量, 每次只能賦予一個成員值?瘓浠八?一個聯合變量的值就是聯合變員的某一個成員值。
[例7.15]設有一個教師與學生通用的表格,教師數據有姓名,年齡,職業,教研室四項。學生有姓名,年齡,職業,班級四項。
編程輸入人員數據, 再以表格輸出。
[code:1:8d8ee8c82c]
main()
{
struct
{
char name[10];
int age;
char job;
union
{
int class;
char office[10];
} depa;
}body[2];

int n,i;
for(i=0;i<2;i++)
{
printf("input name,age,job and department/n");
scanf("%s %d %c",body[i].name,&body[i].age,&body[i].job);
if(body[i].job=='s')

scanf("%d",&body[i].depa.class);
else
scanf("%s",body[i].depa.office);
}
printf("name/tage job class/office/n");
for(i=0;i<2;i++)
{
if(body[i].job=='s')
printf("%s/t%3d %3c %d/n",body[i].name,body[i].age ,body[i].job,body[i].depa.class);
else
printf("%s/t%3d %3c %s/n",body[i].name,body[i].age, body[i].job,body[i].depa.office);
}
}
[/code:1:8d8ee8c82c]
  本例程序用一個結構數組body來存放人員數據, 該結構共有四個成員。其中成員項depa是一個聯合類型, 這個聯合又由兩個成員組成,一個為整型量class,一個為字符數組office。在程序的第一個for語句中,輸入人員的各項數據,先輸入結構的前三個成員name,age和 job,然后判別job成員項,如為"s"則對聯合depa·class輸入(對學生賦班級編號)否則對depa·office輸入(對教師賦教研組名)。

  在用scanf語句輸入時要注重,凡為數組類型的成員,無論是結構成員還是聯合成員,在該項前不能再加"&"運算符。如程序第18行中
body[i].name是一個數組類型,第22行中的body[i].depa.office也是數組類型,因此在這兩項之間不能加"&"運算符。程序中的第二個for語句用于輸出各成員項的值:

本章小結

1. 結構和聯合是兩種構造類型數據,是用戶定義新數據類型的重要手段。結構和聯合有很多的相似之處,它們都由成員組成。成員可以具有不同的數據類型。成員的表示方法相同。都可用三種方式作變量說明。

2. 在結構中,各成員都占有自己的內存空間,它們是同時存在的。一個結構變量的總長度等于所有成員長度之和。在聯合中,所有成員不能同時占用它的內存空間,它們不能同時存在。聯合變量的長度等于最長的成員的長度。

3. “.”是成員運算符,可用它表示成員項,成員還可用“->”運算符來表示。

4. 結構變量可以作為函數參數,函數也可返回指向結構的指針變量。而聯合變量不能作為函數參數,函數也不能返回指向聯合的指針變量。但可以使用指向聯合變量的指針,也可使用聯合數組。

5. 結構定義答應嵌套,結構中也可用聯合作為成員,形成結構和聯合的嵌套。

6. 鏈表是一種重要的數據結構,它便于實現動態的存儲分配。本章介紹是單向鏈表,還可組成雙向鏈表,循環鏈表等。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91在线|亚洲| 国产91九色视频| 91久久精品视频| 欧美日韩在线视频一区| 成人精品一区二区三区电影免费| 国产精品久久久久久中文字| 欧美大全免费观看电视剧大泉洋| 亚洲影院污污.| 日韩亚洲国产中文字幕| 日韩欧美精品网站| 亚洲精品久久久久久久久| 91久热免费在线视频| 亚州欧美日韩中文视频| 在线精品国产欧美| 亚洲欧美日韩中文在线| 精品久久在线播放| 亚洲国产精品yw在线观看| 色偷偷噜噜噜亚洲男人| 午夜精品久久久99热福利| 亚洲综合大片69999| 一区国产精品视频| 精品久久中文字幕| 欧美最猛性xxxxx亚洲精品| 韩曰欧美视频免费观看| 91免费国产视频| 日韩精品在线免费观看| 欧美日韩一区二区精品| 精品国产欧美一区二区五十路| 这里只有精品视频在线| 国产免费一区视频观看免费| 最近更新的2019中文字幕| 欧美极品美女电影一区| 国产精品天天狠天天看| 全色精品综合影院| 亚洲精品综合久久中文字幕| 亚洲一区av在线播放| 精品国产精品三级精品av网址| 亚洲黄色有码视频| 97激碰免费视频| 国产精品一区二区久久久| 热99精品只有里视频精品| 日韩精品在线第一页| 国产日韩欧美在线看| 久久国产加勒比精品无码| 91精品视频在线| 高清视频欧美一级| 啪一啪鲁一鲁2019在线视频| 欧美午夜www高清视频| 91福利视频在线观看| 久久久久成人精品| 91人成网站www| 日韩女优在线播放| 亚洲人成电影网站色| 亚洲精品一区av在线播放| 色悠久久久久综合先锋影音下载| 77777亚洲午夜久久多人| 国产精品揄拍一区二区| 国产精品视频精品视频| 97精品视频在线播放| 欧美最猛性xxxxx(亚洲精品)| 国产精品成人观看视频国产奇米| 国产精品91一区| 992tv成人免费影院| 97视频网站入口| 亚洲国产成人精品久久久国产成人一区| 欧美在线一区二区三区四| 91精品视频在线免费观看| 国产精品入口尤物| 国产91久久婷婷一区二区| 日韩精品免费在线视频| 一区二区av在线| 国产精品第七影院| 国产高清视频一区三区| 欧美人与物videos| 国产综合久久久久| 国产精品女人久久久久久| 亚洲香蕉伊综合在人在线视看| 欧美日韩电影在线观看| 91精品视频免费看| 超碰精品一区二区三区乱码| 久久影院在线观看| 精品性高朝久久久久久久| 国语自产在线不卡| 国产精品日日摸夜夜添夜夜av| 久久久精品一区二区三区| 欧美激情一区二区三区在线视频观看| 亚洲欧美日韩中文在线制服| 国产精品一区二区三区毛片淫片| 91精品久久久久久久久青青| 国产精品久久久久免费a∨大胸| 国模吧一区二区三区| 亚洲一区二区自拍| 久久亚洲精品视频| 日韩hd视频在线观看| 欧美电影《睫毛膏》| 另类天堂视频在线观看| 欧美成年人视频| 中文在线资源观看视频网站免费不卡| 久久福利视频导航| 精品福利一区二区| 91精品国产高清自在线| 成人免费淫片视频软件| 欧美性色xo影院| 国产v综合v亚洲欧美久久| 亚洲精品suv精品一区二区| 国产欧美精品久久久| 96国产粉嫩美女| 91精品久久久久久久久久另类| 欧美日本国产在线| 国产区精品视频| 日韩成人av一区| 91成人在线播放| 欧美激情第三页| 国产97色在线|日韩| www.日韩欧美| 久久国产精品网站| 亚洲高清一二三区| 91精品国产777在线观看| 亚洲影视九九影院在线观看| 亚洲欧洲日产国码av系列天堂| 少妇精69xxtheporn| 日韩成人激情在线| 成人网页在线免费观看| 欧美国产精品日韩| 成人黄色片在线| 欧美激情视频播放| 日韩欧美亚洲一二三区| 日本一区二区在线免费播放| 国产91精品久久久久久久| 日本aⅴ大伊香蕉精品视频| 精品二区三区线观看| 国产成人精品av在线| 欧美大尺度电影在线观看| 国产91精品高潮白浆喷水| 亚州成人av在线| 久久这里有精品| 欧美性少妇18aaaa视频| 亚洲天堂av综合网| 欧美极度另类性三渗透| 亚洲自拍另类欧美丝袜| 国产精品嫩草影院一区二区| 国产99久久久欧美黑人| 精品无人区乱码1区2区3区在线| 欧美成人黑人xx视频免费观看| 久久视频精品在线| 最近2019年好看中文字幕视频| 日韩美女写真福利在线观看| 欧美性猛交xxxx乱大交极品| 国产免费亚洲高清| 久久五月天综合| xxxxx91麻豆| 亚洲欧美另类自拍| 日本一区二区三区四区视频| 亚洲色图狂野欧美| 亚洲免费人成在线视频观看| 国产精品主播视频| 精品av在线播放| 日韩欧美成人免费视频| 国产精品久久久久99| 亚洲精品aⅴ中文字幕乱码| 国产美女久久久| 日韩精品免费视频| 98视频在线噜噜噜国产| 亚洲视频专区在线|