《C語言程序設(shè)計新視角》課件第7章_第1頁
《C語言程序設(shè)計新視角》課件第7章_第2頁
《C語言程序設(shè)計新視角》課件第7章_第3頁
《C語言程序設(shè)計新視角》課件第7章_第4頁
《C語言程序設(shè)計新視角》課件第7章_第5頁
已閱讀5頁,還剩204頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第7章復(fù)合的數(shù)據(jù)類型7.1結(jié)構(gòu)概念的引入 7.2結(jié)構(gòu)體的描述與存儲7.3結(jié)構(gòu)的使用7.4結(jié)構(gòu)體與函數(shù)的關(guān)系7.5共用體7.6枚舉7.7typedef聲明新的類型名7.8本章小結(jié)

【主要內(nèi)容】

給出結(jié)構(gòu)體類型變量的定義、使用規(guī)則及方法實例;

通過結(jié)構(gòu)體與數(shù)組的對比,說明其表現(xiàn)形式與本質(zhì)含義;

通過結(jié)構(gòu)體類型與基本類型的對比,說明其表現(xiàn)形式與本質(zhì)含義;

通過結(jié)構(gòu)成員與普通變量的對比,給出其使用的規(guī)則;

讀程序的訓(xùn)練;

自頂向下算法設(shè)計的訓(xùn)練;

結(jié)構(gòu)的空間存儲特點及調(diào)試要點。

【學(xué)習(xí)目標(biāo)】

理解自定義數(shù)據(jù)類型結(jié)構(gòu)體的意義;

掌握結(jié)構(gòu)體的類型定義、變量定義、初始化、引用的步驟和方法;

掌握結(jié)構(gòu)體與數(shù)組、指針、函數(shù)的關(guān)系;

了解聯(lián)合的概念及其使用;

了解枚舉的概念及其使用。

【引例1】先來回顧一下在第5章“函數(shù)”中舉過的例5-7。

程序?qū)崿F(xiàn):

1 /*一維數(shù)組中求m到n項的和*/

2 #include<stdio.h>

3 intfunc(intb[],intm,intn)

7.1結(jié)構(gòu)概念的引入

4 {

5 inti,sum=0;

6 for(i=m;i<=n;i++)

7 {

8 sum=sum+b[i];/*累加下標(biāo)為m到n的元素*/

9 }

10 returnsum;

11 }

12

13 intmain()

14 {

15 intx,a[]={1,2,3,4,5,6,7,8,9,0};

16 intp=3,q=7;/*下標(biāo)范圍*/

17 x=func(a,p,q);

18 printf("%d\n",x);

19 return0;

20 }我們把上述題目主函數(shù)中的a數(shù)組由一維改為二維,子函數(shù)除了原先的功能外,再添加一個,把累加的結(jié)果放在每行的最后一個元素位置上,見圖7.1。

圖7.1a數(shù)組的信息

1 /*二維數(shù)組求每行m到n項的和*/

2 #include<stdio.h>

3 voidfunc(intb[],intm,intn,intlast);

4

5 voidfunc(intb[],intm,intn,intlast)

6 {

7 inti,sum=0;

8 for(i=m;i<=n;i++)

9 {

10 sum=sum+b[i]; /*累加下標(biāo)為m到n的元素*/

11 }

12 b[last]=sum; /*把累加和放在指定位置*/

13 }

14

15 intmain()

16 {/*a數(shù)組由一維變?yōu)槎S*/

17 inta[][10]={ {1,2,3,4,5,6,7,8,9,0},

18 {2,3,4,5,6,7,8,9,10,0},

19 {3,4,5,6,7,8,9,10,11,0}

20

};

21 intp=3,q=8,last=9;

22 /*a數(shù)組有3行信息,則調(diào)用3次func函數(shù)即可*/

23 for(inti=0;i<3;i++)

24 {

25 func(a[i],p,q,last);

26 }

27

28 for(i=0;i<3;i++) /*輸出a數(shù)組內(nèi)容*/

29 {

30 for(intj=0;j<10;j++)

31 {

32 printf("%4d",a[i][j]);

33 }

34 printf("\n");

35 }

36 return0;

37 }

程序結(jié)果:

12345678939

234567891045

3456789101151

【引例2】把引例1的二維數(shù)組的內(nèi)容擴展成有實際意義的表格,如表7.1所示,求出每個學(xué)生的總分,并填在此表中。

表7.1學(xué)

表與二維數(shù)組相比,這個表格中的數(shù)據(jù)項并不都是同一種類型,要對這樣的表格進行處理,根據(jù)計算機解題的通用規(guī)則,首要的問題是如何把這樣的表格存儲到機器中。計算機解題的通用規(guī)則為:用合適的數(shù)據(jù)結(jié)構(gòu)存儲數(shù)據(jù);用相應(yīng)的算法處理數(shù)據(jù)。

(1)如何將表7.1所示的數(shù)據(jù)表格存儲到機器中?

答:根據(jù)已有數(shù)組存儲的概念,可以把表格中的每列信息都構(gòu)造成一個一維數(shù)組,但這樣做,顯然程序處理起來是非常麻煩的,首先就是主函數(shù)不能方便地把一行信息傳遞給子函數(shù)。借助二維數(shù)組的處理方式,我們希望一次就能傳遞表格的一行信息。

(2)如何把不同類型的一行數(shù)據(jù)組合在一起?

答:表格中的數(shù)據(jù)有多行,只要把一行的信息如何組合存儲的方式分析清楚即可,因為多行信息只是一行的多次重復(fù)。因此,現(xiàn)在解決問題的關(guān)鍵變成,如何把相關(guān)的一組不同類型的數(shù)據(jù)“打包”放在一個連續(xù)的空間,傳遞時只要傳遞這個空間的起始地址即可。

系統(tǒng)要給用戶的“打包”數(shù)據(jù)分配空間,首先就得定義它的存儲尺寸,即數(shù)據(jù)的類型。然而數(shù)據(jù)表中的內(nèi)容是用戶根據(jù)需要確定的,系統(tǒng)無法預(yù)先得知,這就需要用戶自己“構(gòu)造”出數(shù)據(jù)表的類型,然后才能進行存儲空間的分配。

根據(jù)上述結(jié)論,可列出所有已知的條件和希望的結(jié)果:

(1)有多個數(shù)據(jù)項,每個數(shù)據(jù)項都可以用已有的數(shù)據(jù)類型描述;

(2)數(shù)據(jù)項的多少、內(nèi)容是由用戶自己確定的;

(3)希望上述各數(shù)據(jù)項“組合”在一起,有連續(xù)的存儲空間,可以作為一個整體來方便傳址;

(4)要求每個數(shù)據(jù)項可以單獨引用。

根據(jù)上面這些條件和要求以及存儲三要素,可給出這種“組合的數(shù)據(jù)”類型與變量的特征如表7.2所示。

表7.2組合數(shù)據(jù)的特點

說明:類型的名字之所以是“關(guān)鍵字+標(biāo)識符”,是要有一個特別約定的關(guān)鍵詞來表明這是一個類型,又因為這個類型是用戶自己定義的,所以類型名中應(yīng)該有用戶自己命名的成分,以和其他類似的“組合的數(shù)據(jù)”類型名區(qū)別。

這種“組合的數(shù)據(jù)”在C語言中被稱為結(jié)構(gòu)體,其特點如下:

(1)結(jié)構(gòu)體是C語言中的構(gòu)造類型,是由不同數(shù)據(jù)類型的數(shù)據(jù)組成的集合體;

(2)結(jié)構(gòu)體為處理復(fù)雜的數(shù)據(jù)結(jié)構(gòu)提供了手段;

(3)結(jié)構(gòu)體為函數(shù)間傳遞不同類型的參數(shù)提供了便利。

7.2.1結(jié)構(gòu)體的類型定義

結(jié)構(gòu)體(struct):由一系列具有相同類型或不同類型的數(shù)據(jù)構(gòu)成的數(shù)據(jù)集合,也叫結(jié)構(gòu)。

結(jié)構(gòu)體由若干不同類型的數(shù)據(jù)項組成,構(gòu)成結(jié)構(gòu)體的各個數(shù)據(jù)項稱為結(jié)構(gòu)體成員。7.2結(jié)構(gòu)體的描述與存儲結(jié)構(gòu)體類型定義描述結(jié)構(gòu)的組織形式,是一種用戶自定義的類型,同C的基本類型中類型的概念一樣,只是一個存儲尺寸,不分配內(nèi)存。

結(jié)構(gòu)體類型定義形式如下:

struct結(jié)構(gòu)體名

{數(shù)據(jù)類型1成員名1;

數(shù)據(jù)類型2成員名2;

數(shù)據(jù)類型n成員名n;

};說明:

(1)結(jié)構(gòu)體類型名:struct與結(jié)構(gòu)體名合起來表示結(jié)構(gòu)體類型名。struct是關(guān)鍵字,結(jié)構(gòu)體名由用戶用標(biāo)識符標(biāo)示。結(jié)構(gòu)體類型也可簡稱結(jié)構(gòu)類型。

(2)結(jié)構(gòu)體成員:結(jié)構(gòu)體中的一個數(shù)據(jù)項。結(jié)構(gòu)體成員的數(shù)據(jù)類型可以是C語言允

許的所有類型。

【例7-1】結(jié)構(gòu)體類型定義的例子。將圖7.2中的各數(shù)據(jù)項組合在一個結(jié)構(gòu)里。

圖7.2學(xué)生信息數(shù)據(jù)項程序如下:

structstudent /*struct為結(jié)構(gòu)關(guān)鍵字,student為結(jié)構(gòu)名*/

{

intStudentId; /*學(xué)號*/

charStudentName[10]; /*姓名*/

charStudentSex[3]; /*性別*/

intTimeOfEnter; /*入學(xué)時間*/

intScore_1; /*成績1*/

intScore_2; /*成績2*/

intScore_3; /*成績3*/

intScore_4; /*成績4*/

}; 7.2.2結(jié)構(gòu)體變量定義及初始化

結(jié)構(gòu)體變量定義有下面三種格式,在實際編程時可以根據(jù)需要選用。

結(jié)構(gòu)體變量定義格式1:

struct結(jié)構(gòu)類型名變量名表;

結(jié)構(gòu)體變量定義格式2:

struct結(jié)構(gòu)類型名

{類型標(biāo)識符成員名1;

類型標(biāo)識符成員名2;

類型標(biāo)識符成員名n;

}變量名表;結(jié)構(gòu)體變量定義格式3:

struct

{類型標(biāo)識符成員名1;

類型標(biāo)識符成員名2;

類型標(biāo)識符成員名n;

}變量名表;說明:第三種格式用無名結(jié)構(gòu)體直接定義變量只能用一次,無法在后續(xù)程序中再使用這個結(jié)構(gòu)類型。

【例7-2】結(jié)構(gòu)變量定義的例子。結(jié)構(gòu)變量定義語句如下:

structstudentcom[30],*sPtr,x;

結(jié)構(gòu)類型structstudent是在例7-1中已經(jīng)定義的。

定義語句各項的含義見表7.3。

表7.3結(jié)

構(gòu)

關(guān)

結(jié)構(gòu)變量初始化格式如下:

struct結(jié)構(gòu)類型名變量名={初始數(shù)據(jù)}

【例7-3】結(jié)構(gòu)變量初始化的例子。結(jié)構(gòu)類型structstudent是在例7-1中已經(jīng)定義的。

structstudentcom[30]

={{1,"趙壹","男",1999,90,83,72,82},

{2,"錢貳","男",1999,78,92,88,78},

{3,"孫叁","女",1999,89,72,98,66},

{4,"李肆","女",1999,78,95,87,90}

};/*結(jié)構(gòu)數(shù)組的初始化*/

structstudent*sPtr;/*定義結(jié)構(gòu)指針*/

sPtr=com;/*結(jié)構(gòu)指針指向結(jié)構(gòu)數(shù)組*/7.2.3結(jié)構(gòu)體成員引用方法

結(jié)構(gòu)體成員引用有三種方法:

方法一:結(jié)構(gòu)體變量名.成員名;

方法二:結(jié)構(gòu)指針名→成員名;

方法三:(*結(jié)構(gòu)指針名).成員名。

【例7-4】結(jié)構(gòu)體成員引用方法。結(jié)構(gòu)類型定義如下:

structstudent

{

intStudentId;

charStudentName[10];

charStudentSex[3];

intTimeOfEnter;

intScore[4];

inttotal;

}

用structstudent結(jié)構(gòu)類型定義的結(jié)構(gòu)變量、結(jié)構(gòu)成員的引用見表7.4。

表7.4結(jié)構(gòu)成員引用方法7.2.4結(jié)構(gòu)變量的空間分配及查看方法

【例7-5】結(jié)構(gòu)變量、結(jié)構(gòu)指針和結(jié)構(gòu)數(shù)組的查看。

1 #include<stdio.h>

2 intmain()

3 {

4 structstudent

5 {

6 intStudentId;

7 charStudentName[10];

8 charStudentSex[3];

9 intTimeOfEnter;

10 intScore[4];

11 inttotal;

12 };

13

14 structstudentx;

15 structstudentcom[10]

16 ={{1,"趙壹","男",1999,90,83,72,82},

17 {2,"錢貳","男",1999,78,92,88,78},

18 {3,"孫叁","女",1999,89,72,98,66},

19 {4,"李肆","女",1999,78,95,87,90}

20 };

21 structstudent*sPtr;

22

23 sPtr=com;

24 x=com[0];

25 return0;

26 }

【解】(1)結(jié)構(gòu)變量定義:

structstudentx;(程序第14行)

對于變量x,系統(tǒng)將會以什么樣的方式給它分配空間呢?C規(guī)定按結(jié)構(gòu)類型中成員的前后順序及大小來給結(jié)構(gòu)變量分配空間。結(jié)構(gòu)變量X的成員如圖7.3所示。

圖7.3結(jié)構(gòu)變量x的成員結(jié)構(gòu)變量x的空間分配在程序第24行未執(zhí)行前的情形參見圖7.4~圖7.7。圖7.5是點開圖7.4中StudentName前的“+”號,顯示x的成員——數(shù)組Student-Name的元素。

圖7.4結(jié)構(gòu)變量x的空間分配(總)

圖7.5結(jié)構(gòu)變量x的空間分配(1)

圖7.6結(jié)構(gòu)變量x的空間分配(2)

圖7.7結(jié)構(gòu)變量x的空間分配(3)

(2)結(jié)構(gòu)數(shù)組定義:

structstudentcom[10];(程序第15行至第20行)

指針賦值:

sPtr=com;(程序第23行)

sPtr執(zhí)向com的位置及sPtr偏移9后指向的位置見圖7.8。

圖7.8結(jié)構(gòu)數(shù)組com的空間示意圖

圖7.9結(jié)構(gòu)數(shù)組com的空間分配

圖7.10結(jié)構(gòu)指針sPtr的指向

圖7.11結(jié)構(gòu)指針sPtr+3的指向

圖7.12x=com[0]賦值后的情形

內(nèi)存地址對齊(alignment)

為了提高CPU訪問內(nèi)存的效率,程序語言的編譯器在做變量的存儲分配時就進行了分配優(yōu)化處理,對于基本類型的變量,其優(yōu)化規(guī)則(也稱為“對齊”規(guī)則)如下:

變量地址%N=0

其中對齊參數(shù)N=sizeof(變量類型)。

注:不同的編譯器,具體的處理規(guī)則可能不一樣。

結(jié)構(gòu)體空間分配規(guī)則(VC++6.0環(huán)境)

1)結(jié)構(gòu)成員存放順序

結(jié)構(gòu)體的成員在內(nèi)存中順序存放,所占內(nèi)存地址依次增高,第一個成員處于低地址處,最后一個成員處于最高地址處。

2)結(jié)構(gòu)對齊參數(shù)

(1)結(jié)構(gòu)體一個成員的對齊參數(shù):

N=min(sizeof(該成員類型),n)

注:n為VC++6.0中可設(shè)置的值,默認為8字節(jié)。

(2)結(jié)構(gòu)體的對齊參數(shù)M:M=結(jié)構(gòu)體中所有成員的對齊參數(shù)中的最大值。

3)結(jié)構(gòu)體空間分配規(guī)則

(1)結(jié)構(gòu)體長度L:滿足條件L%M=0(不夠要補足空字節(jié))。

(2)每個成員地址x:滿足條件x%N=0(空間剩余,由下一個成員做空間補充)。結(jié)構(gòu)內(nèi)的成員空間分配以M為單位開辟空間單元;若成員大小超過M,則再開辟一個M單元;若此單元空間剩余,則由下一個成員按對齊規(guī)則做空間補充(結(jié)構(gòu)體嵌套也是一樣的規(guī)則)。

【例7-6】結(jié)構(gòu)存儲空間及內(nèi)存對齊的查看例子1。結(jié)構(gòu)體成員是基本數(shù)據(jù)類型時的內(nèi)存對齊。

struct

{

short

a1;

short

a2;

short

a3;

}A={1,2,3};

struct

{

long

a1;

short

a2;

}B={4,5};

struct

{

shorta1;

longa2;

}C={6,7};

sizeof(A)=6,sizeof(B)=8,sizeof(C)=8,為什么是這樣?

注:sizeof(short)=2,sizeof(long)=4。

圖7.13為例7-6的查看步驟1,結(jié)構(gòu)體A的對齊參數(shù)M=sizeof(short)=2(byte)。

圖7.13例7-6查看步驟1

圖7.14例7-6查看步驟2

圖7.15例7-6查看步驟3

【例7-7】結(jié)構(gòu)存儲空間及內(nèi)存對齊的查看例子2。結(jié)構(gòu)體成員是構(gòu)造數(shù)據(jù)類型時的內(nèi)存對齊。設(shè)

structstudentx={1,"趙壹","男",3,4,5,6,7};

圖7.16中,Memory窗口的地址是結(jié)構(gòu)變量x的起始地址。

圖7.16例7-7查看步驟

x的存儲空間長度=0x12ff7c-0x12ff58+4=0x28=40(byte)

x成員定義長度和=(int+char*10+char*3+int+int*4)=37(byte)

二者相差3

byte,即存在如圖7.17所示的內(nèi)存“空洞”,這是如何產(chǎn)生的呢?

結(jié)構(gòu)體x的對齊參數(shù)M=sizeof(int)=4

注意:

(1)0x12FF64、0x12FF65兩個單元放的是StudentName[8]和StudentName[9]。

圖7.17內(nèi)存“空洞”

(2)StudentSex的起始地址是0x12FF66。因為StudentSex的對齊參數(shù)N=min(sizeof(該成員類型),8)=sizeof(char)=1,0x12FF66%N=0,所以StudentSex的三個元素從0x0x12FF66開始存儲。

(3)TimeOfEnter的起始地址是0x12FF6C。因為TimeOfEnter的對齊參數(shù)

N=sizeof(int)=4,StudentSex存儲后的起始地址是0x12FF69,0x12FF69至0x12FF6B都不是4的整數(shù)倍

(如圖7.18所示),而0x12FF6C是4的整數(shù)倍,所以TimeOfEnter的起始地址是0x12FF6C。因此,StudentSex后的3

byte內(nèi)存“空洞”是由于TimeOfEnter的“對齊”產(chǎn)生的。圖7.18地址模4運算

【例7-8】結(jié)構(gòu)與數(shù)組類比的例子。請寫出一個程序,找出表7.5中的最高成績,顯示此組信息,并將之與表中的第一列信息交換。7.3結(jié)?構(gòu)?的?使?用

表7.5數(shù)據(jù)表

表格中的數(shù)據(jù)如何存儲?

答:根據(jù)計算機解題的通用規(guī)則,我們首先要解決的問題是如何把表格中的數(shù)據(jù)按什么形式存儲到機器中,然后才能在相應(yīng)的存儲結(jié)構(gòu)中進行算法處理。

數(shù)據(jù)結(jié)構(gòu)設(shè)計可采用以下三種方式:

(1)用一維數(shù)組。

成績數(shù)組:intscore[6]={90,80,65,95,75,97};

座位數(shù)組:intset[6]={1,2,3,4,5,6};

(2)用二維數(shù)組。成績與座位的組合:

intscore[2][6]={{90,80,65,95,75,97},{1,2,3,4,5,6}};

用一維或二維數(shù)組的方式存儲數(shù)據(jù)的規(guī)則我們已經(jīng)熟悉了,即把同類型的數(shù)據(jù)按序存放,元素用數(shù)組名配合下標(biāo)引用。

(3)用結(jié)構(gòu)。

方式1:

structnode{

intscore[6];

intset[6];}

structnodex={{90,80,65,95,75,97},{1,2,3,4,5,6}}

方式2:

structnode{

intscore;

intset;}

structnodey[6]={{90,1},{80,2},{65,3},{95,4},{75,5},{97,6}};用結(jié)構(gòu)的方式存儲的思路是,把相關(guān)的一組數(shù)據(jù)“打包”放在一起。結(jié)構(gòu)的類型是用戶自己定義的,結(jié)構(gòu)的空間是在定義結(jié)構(gòu)類型變量時分配的。

按照結(jié)構(gòu)的形式,把數(shù)據(jù)存儲到內(nèi)存后,要對它們進行處理,就引出了數(shù)據(jù)如何引用的問題。表7.6給出了一維數(shù)組、二維數(shù)組、結(jié)構(gòu)三種數(shù)據(jù)的組織形式以及其中數(shù)據(jù)項的引用形式。

表7.6數(shù)據(jù)存儲及引用

表7.7x與y的存儲

表7.8例7-8偽代碼

【方法1】

1 /*例7-8用一維數(shù)組實現(xiàn)*/

2 #include<stdio.h>

3 #defineMAX6

4

5 intmain()

6 {

7 intscore[MAX]={90,80,65,95,75,97};

8 intset[MAX]={1,2,3,4,5,6};

9 intmax,num;

10 inttemp1,temp2;

11

12 /*在score中找最大值,并將之記錄在max中,對應(yīng)下標(biāo)值記錄在num中*/

13 max=score[0];/*取第一組值做比較基準(zhǔn)*/

14 num=1;

15 for(inti=1;i<MAX;i++)

16 {

17 if(max<score[i])

18 {

19 max=score[i];

20 num=set[i];

21 }

22 }

23

24 /*最大值與第一個值交換*/

25 temp1=score[0];

26 temp2=set[0];

27 score[0]=max;

28 set[0]=num;

29 score[num-1]=temp1;

30 set[num-1]=temp2;

31

32 /*輸出*/

33 printf("第1名:%d號,%d分\n",set[0],score[0]);

34 return0;

35 }

程序結(jié)果:

第1名:6號,97分

【方法2】

1 /*例7-8用二維數(shù)組實現(xiàn)*/

2 #include<stdio.h>

3 #defineMAX6

4 intmain()

5 {

6 intscore[2][MAX]=

7 {{90,80,65,95,75,97},

8 {1,2,3,4,5,6}

9 };

10 intmax,num;

11 inttemp1,temp2;

12

13 /*在score中找最大值,并將之記錄在max中,對應(yīng)下標(biāo)值記錄在n中*/

14 max=score[0][0];/*取第一組值做比較基準(zhǔn)*/

15 num=1;

16 for(inti=1;i<MAX;i++)

17 {

18 if(max<score[0][i])

19 {

20 max=score[0][i];

21 num=score[1][i];

22 }

23 }

24

25 /*最大值與第一個值交換*/

26 temp1=score[0][0];

27 temp2=score[1][0];

28 score[0][0]=max;

29 score[1][0]=num;

30 score[0][num-1]=temp1;

31 score[1][num-1]=temp2;

32

33 /*輸出*/

34 printf("第1名:%d號,%d分\n",score[1][0],score[0][0]);

35 return0;

36 }

程序結(jié)果:

第1名:6號,97分

【方法3】

1 /*例7-8用結(jié)構(gòu)方式1實現(xiàn)*/

2 #include<stdio.h>

3 #defineMAX6

4 intmain()

5 {

6 structnode

7 {

8 intscore[MAX];

9 intset[MAX];

10 }x={{90,80,65,95,75,97},{1,2,3,4,5,6}};

11 intmax,num;

12 inttemp1,temp2;

13

14 /*在score中找最大值,并將之記錄在m中,對應(yīng)下標(biāo)值記錄在n中*/

15 max=x.score[0];/*取第一組值做比較基準(zhǔn)*/

16 num=1;

17 for(inti=1;i<MAX;i++)

18 {

19 if(max<x.score[i])

20 {

21 max=x.score[i];

22 num=x.set[i];

23 }

24 }

25

26 /*最大值與第一個值交換*/

27 temp1=x.score[0];

28 temp2=x.set[0];

29 x.score[0]=max;

30 x.set[0]=num;

31 x.score[num-1]=temp1;

32 x.set[num-1]=temp2;

33

34 /*輸出*/

35 printf("第1名:%d號,%d分\n",x.set[0],x.score[0]);

36 return0;

37 }

程序結(jié)果:

第1名:6號,97分

【方法4】

1 /*用結(jié)構(gòu)方式2實現(xiàn)*/

2 #include<stdio.h>

3 #defineMAX6

4 intmain()

5 {

6 structnode

7 {

8 intscore;

9 intset;

10 }y[6]={{90,1},{80,2},{65,3},{95,4},{75,5},{97,6}};

11 intmax,num;

12 inttemp1,temp2;

13

14 /*在score中找最大值,并將之記錄在m中,對應(yīng)下標(biāo)值記錄在n中*/

15 max=y[0].score;/*取第一組值做比較基準(zhǔn)*/

16 num=1;

17 for(inti=1;i<MAX;i++)

18 {

19 if(max<y[i].score)

20 {

21 max=y[i].score;

22 num=y[i].set;

23 }

24 }

25

26 /*最大值與第一個值交換*/

27 temp1=y[0].score;

28 temp2=y[0].set;

29 y[0].score=max;

30 y[0].set=num;

31 y[num-1].score=temp1;

32 y[num-1].set=temp2;

33

34 /*輸出*/

35 printf("第1名:%d號,%d分\n",y[0].set,y[0].score);

36 return0;

37 }

程序結(jié)果:

第1名:6號,97分

【例7-9】順序結(jié)構(gòu)程序的例子的改進。從鍵盤輸入四個學(xué)生的學(xué)號和英語考試成績,打印這四人的學(xué)號和成績,最后輸出四人的英語平均成績。

1 /*順序結(jié)構(gòu)程序的例子的改進*/

2 #include<stdio.h>

3 intmain()

4 {

5 structnode

6 {

7 intnumber[4];

8 floatgrade[4];

9 }stu;

10

11 floatgrade=0;

12 inti;

13 /*輸入四個學(xué)生的學(xué)號和英語考試成績*/

14 for(i=0;i<4;i++)

15 {

16 printf("inputnumber:\n");

17 scanf("%d",&stu.number[i]);

18 printf("inputgrader:\n");

19 scanf("%f",&stu.grade[i]);

20 grade=grade+stu.grade[i];

21 }

22 /*打印四人的學(xué)號和成績及平均成績*/

23 printf("numbergrade\n");

24 for(i=0;i<4;i++)

25 {

26 printf("%d:%0.1f\n",stu.number[i],stu.grade[i]);

27 }

28 printf("average=%0.1f\n",grade/4);

29 return0;

30 }

程序結(jié)果:

inputnumber:

101

inputgrader:

98

inputnumber:

102

inputgrader:

87

inputnumber:

103

inputgrader:

67

inputnumber:

104

inputgrader:

92

numbergrade

101:98.0

102:87.0

103:67.0

104:92.0

average=86.0

把數(shù)據(jù)按照結(jié)構(gòu)的形式存儲后,這個程序從形式上看,比“順序結(jié)構(gòu)程序”要簡潔。

【例7-10】結(jié)構(gòu)的例子2。請設(shè)計一個統(tǒng)計選票的程序。現(xiàn)設(shè)有三個候選人的名單,見表7.9,請分別統(tǒng)計出他們各得票的多少。由鍵盤輸入候選人的名字來模擬唱票過程,選票數(shù)為N(注:每次只能從三個候選者中選擇一人)。

表7.9選票【解】偽代碼見表7.10。

表7.10例7-10偽代碼

1 /*統(tǒng)計選票*/

2 #include<stdio.h>

3 #include<string.h>

4 #defineN50/*投票人數(shù)*/

5 structperson

6 { charname[20];/*候選人姓名*/

7 intsum;/*得票數(shù)*/

8 }

9

10 intmain()

11 {

12 structpersona[3]

13 ={"Zhang",0,"Tong",0,"Wang",0};/*選票結(jié)構(gòu)*/

14 inti,j;

15 charin_name[20];

16

17 for(i=1;i<=N;i++)/*N位投票人,處理N次*/

18 {

19 scanf("%s",in_name);/*輸入候選人名*/

20 for(j=0;j<3;j++)/*選中的候選者得票數(shù)加1*/

21 if(strcmp(in_name,a[j].name)==0)

22 {

23 a[j].sum++;

24 }

25 }

26 for(i=0;i<3;i++)/*輸出結(jié)果*/

27 {

28 printf("%s,%d\n",a[i].name,a[i].sum);

29 }

30 return0;

31 }

【例7-11】結(jié)構(gòu)的例子3——對“函數(shù)讀程練習(xí)1的例子”做一改進。

原題目:處理3個學(xué)生四門課程的成績,成績存儲在二維數(shù)組studentGrades中。

(1)求所有成績中的最低、最高成績;

(2)每個學(xué)生的平均成績;

(3)輸出結(jié)果。

intstudentGrades[STUDENTS][EXAMS]

=

{{

77,68,86,73

},

{

96,87,89,78

},

{

70,90,86,81

}

};

改進的題目:已知N個學(xué)生的學(xué)號、姓名及四門課程的成績,如表7.11所示。

(1)求所有成績中的最低、最高成績;

(2)求每個學(xué)生的總成績、平均成績;

(3)打印全班成績單。

表7.11學(xué)

【解】

(1)數(shù)據(jù)存放:選結(jié)構(gòu)存儲。

#defineN50

structstu

{

intnum;

charname[10];

floatmath;

floatphys;

floateng;

floatpro;

floattotal;

floatave;

};

structstua[N]

={ {1,"Zhao",77,68,86,73,0,0},

{2,"Qian",96,87,89,78,0,0},

{3,"Sun",70,90,86,81,0,0}

};

(2)算法描述:偽代碼見表7.12。

表7.12例7-11偽代碼

(3)程序?qū)崿F(xiàn)關(guān)鍵點分析:

inti;

floatlowGrade=100; /*初始化為可能的最高分數(shù)*/

floathighGrade=0; /*初始化為可能的最低分數(shù)*/

floattotal=0;

/*找比lowGrade低的值,記在lowGrade中*/

if(lowGrade>a[i].math)lowGrade=a[i].math;

if(lowGrade>a[i].phys)lowGrade=a[i].phys;

if(lowGrade>a[i].eng)lowGrade=a[i].eng;

if(lowGrade>a[i].pro)lowGrade=a[i].pro;

/*找比highGrade低的值,記在highGrade中*/

if(highGrade<a[i].math)highGrade=a[i].math;

if(highGrade<a[i].phys)highGrade=a[i].phys;

if(highGrade<a[i].eng)highGrade=a[i].eng;

if(highGrade<a[i].pro)highGrade=a[i].pro;

total=a[i].math+a[i].phys+a[i].eng+a[i].pro;

a[i].total=total; /*在表中填總成績*/

a[i].ave=total/4; /*在表中填平均成績*/

每項成績的引用形式都很繁瑣,最好能以一種簡潔的方式表示各項成績。如何改進?

改進方法:設(shè)置一個gradePtr指針,指向第一個成績,見圖7.19。

圖7.19對成績引用的改進

gradePtr[0]=a[0].math;

gradePtr[1]=a[0].phys;

gradePtr[2]=a[0].eng;

gradePtr[3]=a[0].pro;

因為在結(jié)構(gòu)中,成績各項的數(shù)據(jù)類型都是相同的,所以也是連續(xù)存儲的。各項成績通過指針的引用,就變得有規(guī)律了。

float*gradePtr;

gradePtr=&a[0].math;

total=0;

for(i=0;i<4;i++) /*循環(huán)結(jié)構(gòu)中一個學(xué)生的各科成績*/

{

if(gradePtr[i]<lowGrade)lowGrade=gradePtr[i];

if(gradePtr[i]>highGrade)highGrade=gradePtr[i];

total+=gradePtr[i];

}

a[0].total=total; /*在表中填總成績*/

a[0].ave=total/4; /*在表中填平均成績*/

或者

float*gradePtr;

gradePtr=&a[0].math;

total=0;

for(i=0;i<4;i++,gradePtr++)

{

if(*gradePtr<lowGrade)lowGrade=*gradePtr;

if(*gradePtr>highGrade)highGrade=*gradePtr;

total+=*gradePtr;

}

a[0].total=total;

a[0].ave=total/4;

完整的程序如下:

1 /*對結(jié)構(gòu)表中成績的統(tǒng)計*/

2 #include<stdio.h>

3 #defineN3

4 structstu

5 {intnum;

6 charname[10];

7 floatmath;

8 floatphys;

9 floateng;

10 floatpro;

11 floattotal;

12 floatave;

13 };

14

15 intmain()

16 {

17 structstua[N]

18 ={ {1,"Zhao",77,68,86,73,0,0},

19 {2,"Qian",96,87,89,78,0,0},

20 {3,"Sun",70,90,86,81,0,0}

21 };

22 float*gradePtr;

23 floatlowGrade=100; /*初始化為可能的最高分數(shù)*/

24 floathighGrade=0; /*初始化為可能的最低分數(shù)*/

25 floattotal=0; /*考試成績總和*/

26

27 for(intj=0;j<N;j++) /*循環(huán)結(jié)構(gòu)中的行*/

28 {

29 gradePtr=&a[j].math;

30 total=0;

31 for(inti=0;i<4;i++) /*循環(huán)結(jié)構(gòu)中一行的各個成績*/

32 {

33 if(gradePtr[i]<lowGrade)lowGrade=gradePtr[i];

34

if(gradePtr[i]>highGrade)highGrade=gradePtr[i];

35 total+=gradePtr[i];

36 }

37 a[j].total=total;

38 a[j].ave=total/4; /*計算平均成績*/

39 }

40 printf("\nLowestrade:%.1f\nHighestgrade:%.1f\n\n",

41 lowGrade,highGrade);

42 /*輸出成績表格*/

43 printf("numnamemath.P.totalave\n");

44 for(inti=0;i<N;i++)

45 {

46 printf("%3d%6s%6.1f%6.1f%6.1f%6.1f%6.1f%6.1f\n",

47

a[i].num,a[i].name,a[i].math,a[i].phys,a[i].eng,

48 a[i].pro,a[i].total,a[i].ave);

49 }

50 return0;

51 }/*結(jié)束main*/

程序結(jié)果:

Lowestgrade:68.0

Highestgrade:96.0

numnamemath.Phys.eng.pro.totalave

1Zhao77.068.086.073.0304.076.0

2Qian96.087.089.078.0350.087.5

3Sun70.090.086.081.0327.081.8

和普通變量一樣,結(jié)構(gòu)體變量、結(jié)構(gòu)體指針均可作為函數(shù)的參數(shù)和返回值,具體情形參見表7.13。7.4結(jié)構(gòu)體與函數(shù)的關(guān)系

表7.13函數(shù)中結(jié)構(gòu)體作參數(shù)的方式

【例7-12】結(jié)構(gòu)變量作形參的例子。

1 /*結(jié)構(gòu)變量做形參*/

2 #include<stdio.h>

3

4 structstudent

5 { intnum;

6 floatgrade;

7 };

8

9 structstudentfunc1(structstudentstu) /*形參為結(jié)構(gòu)變量*/

10 {

11 stu.num=101;

12 stu.grade=86;

13 return(stu); /*返回結(jié)構(gòu)變量*/

14 }

15

16 intmain()

17 {

18 structstudentx={0,0};

19 structstudenty;

20

21 y=func1(x); /*實際參數(shù)為整個結(jié)構(gòu)變量*/

22 return0;

23}

圖7.20~圖7.23分別為例7-12的調(diào)試步驟1~調(diào)試步驟4。

圖7.20例7-12調(diào)試步驟1圖7.21中,值傳遞,形參stu的地址為0x12ff14,與實參存儲單元不是同一個。實參的值被拷貝了一份,放在形參中。

圖7.22中,結(jié)構(gòu)成員在子函數(shù)func1中被修改。

圖7.23中,返回主函數(shù),結(jié)構(gòu)變量y接收func1返回的結(jié)構(gòu)變量的值。

圖7.21例7-12調(diào)試步驟2

圖7.22例7-12調(diào)試步驟3

圖7.23例7-12調(diào)試步驟4注意:x、y與stu三者的存儲單元地址都是各分單元的,x的值并未被修改。

【例7-13】返回值是結(jié)構(gòu)指針的例子。

1 /*返回值是結(jié)構(gòu)指針*/

2 #include<stdio.h>

3

4 structstudent

5 {intnum;

6 floatgrade;

7 };

8

9 structstudent*func2(structstudentstu)

10 {

11 structstudent*str=&stu;

12 str->num=101;

13 str->grade=86;

14 return(str); /*返回結(jié)構(gòu)指針*/

15 }

16

17 intmain()

18 {

19 structstudentx={0,0};

20 structstudent*stuPtr;

21

22 stuPtr=func2(x);

23 return0;

24 }圖7.24~圖7.31分別為例7-13的調(diào)試步驟1~調(diào)試步驟8。

圖7.24中,注意實參x的地址為0x12ff78。

圖7.25中,注意形參stu的地址為0x12ff20。

圖7.26中,修改stu結(jié)構(gòu)中的成員值。

圖7.27中,主函數(shù)中sutPtr接收返回的局部量str的值。

圖7.24例7-13調(diào)試步驟1

圖7.25例7-13調(diào)試步驟2

圖7.26例7-13調(diào)試步驟3

圖7.27例7-13調(diào)試步驟4注意:在第6章“指針”中討論過關(guān)于不要返回局部量的地址的問題。此程序若以結(jié)構(gòu)體指針為參數(shù),可以改進如下:

1 /*以結(jié)構(gòu)體指針為形式參數(shù)*/

2 #include<stdio.h>

3

4 structstudent

5 {intnum;

6 floatgrade;

7 };

8

9 voidfunc3(structstudent*str)

10 {

11 str->num=101;

12 str->grade=86;

13 }

14

15 intmain()

16 {

17 structstudentx={0,0};

18

19 func3(&x);

20 return0;

21}

圖7.28~圖7.31為改進后程序的調(diào)試步驟。

圖7.28中,實參為結(jié)構(gòu)變量的地址0x12ff78。

圖7.29中,形參為結(jié)構(gòu)指針,指向?qū)崊卧獂。

圖7.30中,子函數(shù)func3修改0x12ff78地址中的結(jié)構(gòu)成員數(shù)據(jù)。

圖7.28例7-13調(diào)試步驟5

圖7.29例7-13調(diào)試步驟6

圖7.30例7-13調(diào)試步驟7

圖7.31例7-13調(diào)試步驟8

結(jié)構(gòu)變量與結(jié)構(gòu)指針在函數(shù)的信息傳遞中的使用方法及原則與普通變量及指針是一樣的。

【例7-14】結(jié)構(gòu)成員作形參的例子。已知一個班學(xué)生信息如表7.14所示,要求在主函數(shù)中賦初值及打印結(jié)果,在子函數(shù)中求出一個人的總成績和平均成績。

表7.14成績表

【解】(1)數(shù)據(jù)存放:結(jié)構(gòu)存儲。

#defineN50

structstu

{intnum;

charname[10];

floatmath;

floatphys;

floateng;

floatpro;

floattotal;

floatave;

};

structstua[N]

={ {1,"Zhao",77,68,86,73,0,0},

{2,"Qian",96,87,89,78,0,0},

{3,"Sun",70,90,86,81,0,0}

};

(2)算法分析:子函數(shù)的功能是實現(xiàn)總分和均分的計算,因此只需把各科成績信息傳遞給子函數(shù)即可,結(jié)構(gòu)中的學(xué)號、姓名等信息是不必傳遞的。設(shè)

structstup;

float*gradePtr;

p=a; /*ptr指針指向結(jié)構(gòu)表格的一行*/

gradePtr=&a[0].math; /*gradePtr指向成績起始地址*/

指針gradePtr和p的指向見圖7.32。

圖7.32數(shù)據(jù)及引用成績信息傳遞方法設(shè)計:由于各科的成績數(shù)據(jù)類型都一樣,在結(jié)構(gòu)中是連續(xù)存儲的,因此把成績信息作地址傳遞即可。子函數(shù)設(shè)計見表7.15。

表7.15例7-14子函數(shù)設(shè)計說明:

(1)輸入信息是成績,有多個,可能的方案有傳值和傳址兩種,這里采用傳址方式;形式參數(shù)為float型指針,實際參數(shù)為結(jié)構(gòu)成員math的地址。

(2)輸出信息有兩個,這里采用形參共用地址的方式,故就不用return了,因此函數(shù)的類型為void。

(3)程序?qū)崿F(xiàn):

1 /*結(jié)構(gòu)成員作形參*/

2 #include<stdio.h>

3 #defineN3

4 voidcalculate(float*gradePtr);

5

6 structstu

7 { intnum;

8 char*name;

9 floatmath;

10 floatphys;

11 floateng;

12 floatpro;

13 floattotal;

14 floatave;

15 };

16

17 intmain()

18 {

19 structstua[N]

20 ={ {1,"Zhao",77,68,86,73,0,0},

21 {2,"Qian",96,87,89,78,0,0},

22 {3,"Sun",70,90,86,81,0,0}

23 };

24 structstu*p=a;

25 inti;

26

27 printf("NameNum.Math.Phys.Eng.Pro.Total.Ave\n");

28 for(p=a,i=0;i<N;i++,p++)

29 {

30 calculate(&(*p).math);

31 printf("%4d%6s%7.2f%7.2f%7.2f%7.2f%7.2f%7.2f\n",

32 (*p).num,(*p).name,(*p).math,(*p).phys,

33 (*p).eng,(*p).pro,(*p).total,(*p).ave);

34 }

35 return0;

36 }

37

38 /*計算一個學(xué)生的總成績和平均成績*/

39 voidcalculate(float*gradePtr)

40 {

41 floatsum=0;

42 inti;

43 for(i=0;i<4;++i,++gradePtr)

44 {

45 sum+=*gradePtr;

46 }

47 *gradePtr=sum; /*求總成績*/

48 gradePtr++;

49 *gradePtr=sum/4; /求平均成績*/

50 }

程序結(jié)果:

NameNum.Math.Phys.Eng.Pro.Total.Ave

1Zhao77.0068.0086.0073.00304.0076.00

2Qian96.0087.0089.0078.00350.0087.50

3Sun70.0090.0086.0081.00327.0081.75

共用體(聯(lián)合體)是指幾個不同時出現(xiàn)的變量成員共享一塊內(nèi)存單元。當(dāng)若干變量每次只使用其中之一時,可以采用“共用體”(union)數(shù)據(jù)結(jié)構(gòu)。給共用體數(shù)據(jù)中各成員分配同一段內(nèi)存單元,設(shè)置這種數(shù)據(jù)類型的主要目的就是節(jié)省內(nèi)存。7.5共用體

1.共用體類型的定義

共用體類型定義的一般形式為:

union共用體名

{

類型名1成員名1;

類型名2成員名2;

類型名n成員名n;

}說明:

(1)union為關(guān)鍵字。

(2)和struct聲明一樣,union聲明僅僅創(chuàng)建了一個類型,在任何函數(shù)之外加union和struct聲明,并不會創(chuàng)建實際變量。

例如:

unionnumber

{

intx;

charch;

floaty;

};

與struct成員不同的是,union中的成員x、ch和y具有同樣的地址,如圖7.33所示。sizeof(unionnumber)取決于占空間最多的成員變量y。

圖7.33共用體成員共用同一個地址共用體的特點及與結(jié)構(gòu)體的關(guān)系見表7.16。

表7.16共用體的特點及與結(jié)構(gòu)體的關(guān)系2.共用體變量的定義

與結(jié)構(gòu)體變量相同,共用體變量定義的一般形式為:

union共用體名

{

類型名1成員名1;

類型名2成員名2;

類型名n成員名n;

}變量名表;

3.共用體成員的引用方式

共用體成員引用方式有兩種:

方式一:共用體變量名.成員名;

方式二:共用體指針名->成員名。

【例7-15】共用體的例子1。

#include<stdio.h>

intmain()

{

unionnumber /*定義共用體類型*/

{

intx;

charch;

floaty;

};

unionnumberunit; /*定義共用體變量*/

unit.x=1; /*共用體成員引用*/

unit.ch='a';

unit.y=2;

return0;

}

圖7.34~圖7.36所示分別為例7-15的調(diào)試步驟1~調(diào)試步驟3。

由圖7.34可以看出三個共用體成員的地址都是0x12ff7c,其中顯示了給成員x賦值1時的情形。

圖7.35顯示了給成員ch賦值‘a(chǎn)’

時的情形。

圖7.36顯示了給成員y賦值2時的情形。注意Memory中的值顯示的是0x40000000,這是什么原因呢?請看下面的“思考與討論”。

圖7.34例7-15調(diào)試步驟1

圖7.35例7-15調(diào)試步驟2

圖7.36例7-15調(diào)試步驟3

圖7.36中,float型變量y的值是2,為什么在內(nèi)存中顯示為0x40000000?

答:根據(jù)第2章中介紹的IEEE754標(biāo)準(zhǔn),按float占32位的情形,實數(shù)2的存儲形式見表7.17,即為0x40

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論