版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
第九章結(jié)構(gòu)體和共用體9.1結(jié)構(gòu)體9.2嵌套結(jié)構(gòu)9.3結(jié)構(gòu)體型數(shù)組9.4結(jié)構(gòu)體型指針9.5結(jié)構(gòu)體與函數(shù)9.6內(nèi)存的動態(tài)分配9.7共用體(聯(lián)合)9.8位段9.9類型定義9.10程序設計舉例習題9.1結(jié)構(gòu)體9.1.1結(jié)構(gòu)體類型在第六章我們討論了使用數(shù)組的好處。我們按順序讀入了5個學生某門課的成績,并按相反的順序輸出。我們并沒有定義score1,score2,score3,score4,score5,而是定義了一個可以存儲全部5門課成績的數(shù)組,即floatscore[5],針對數(shù)組元素score[0],…,score[4]編程實現(xiàn)了對5門課成績的處理。正是因為這些數(shù)據(jù)項的數(shù)據(jù)類型完全相同,并且具有相同的含義,所以可以用數(shù)組來組織它們。然而,在實際處理中,待處理的信息往往是由多種類型組成的,如有關學生的數(shù)據(jù),不僅有學習成績,還應包括諸如學號(長整型)、姓名(字符串類型)、性別(字符型)、出生日期(字符串型)等。再如有關職工的管理程序,所處理的對象——職工的信息同樣包含了職工編號、職工姓名、工種等多種類型數(shù)據(jù)。就目前所學知識,我們只能將各個項定義成互相獨立的簡單變量或數(shù)組,無法反映它們之間的內(nèi)在聯(lián)系。對此,應該有一種新的類型,就像數(shù)組將多個同類型數(shù)據(jù)組合在一起一樣,能將這些具有內(nèi)在聯(lián)系的不同類型的數(shù)據(jù)組合在一起,C語言提供了“結(jié)構(gòu)體(Structure)”類型來完成這一任務。9.1.2結(jié)構(gòu)體類型的定義結(jié)構(gòu)體類型不像整型一樣已經(jīng)由系統(tǒng)定義好,可以直接用來定義整型變量,而是需要編程者自己先定義結(jié)構(gòu)體類型(即結(jié)構(gòu)體的組成)。C語言提供了關鍵字struct來標識所定義的是結(jié)構(gòu)體類型,具體的類型名要編程者根據(jù)需要自己定義。結(jié)構(gòu)體類型規(guī)定了該結(jié)構(gòu)體所包含的成員。結(jié)構(gòu)體類型的定義形式及其例子如圖9.1所示。圖9.1結(jié)構(gòu)體類型定義語法說明:
(1)關鍵字struct告訴我們尾隨的是一個結(jié)構(gòu)體。結(jié)構(gòu)體類型的名字為student,名字也稱為標記(Tag),用于識別不同的結(jié)構(gòu)體類型。關鍵字和類型名字組合成一種新的類型標識符,其地位如同通常的int,char等,其用途是可以定義該結(jié)構(gòu)體型變量。結(jié)構(gòu)體類型定義之后,就可以像其它類型一樣使用了。類型名或者標記的起名規(guī)則遵從標識符命名規(guī)則。
(2)成員列表為本結(jié)構(gòu)體類型所包含的若干個成員的列表,必須用{}括起來,并以分號結(jié)束。每個成員的形式如下:類型標識符成員名;如上例中的
longintnum;
charname[20];成員(如num)又可稱為成員變量,也是一種標識符,成員的類型可以是除該結(jié)構(gòu)體類型自身外,C語言允許的任何數(shù)據(jù)類型。結(jié)構(gòu)體類型structstudent中包含四個成員,分別是:學號num(長整型)、姓名name(字符數(shù)組)、性別sex(字符型)、年齡(整型)。
(3)同一結(jié)構(gòu)體類型中的各成員不可互相重名,但不同結(jié)構(gòu)體類型間的成員可以重名,并且成員名還可以與程序中的變量重名,因為它們代表不同的對象。初學者為清晰起見,最好都不重名。9.1.3結(jié)構(gòu)體型變量的定義結(jié)構(gòu)體類型的定義本身不會創(chuàng)建任何變量。它只是一種模板(Template),限定了這種類型的結(jié)構(gòu)體變量的組成樣式。有了結(jié)構(gòu)體類型,便可以在此類型基礎上定義變量了。結(jié)構(gòu)體類型變量因其所基于的類型是自定義的,所以有多種形式的定義方法。下面以表示職工工資單的結(jié)構(gòu)體類型structstaff為例來說明三種定義變量的方法。形式一:類型、變量分別定義。
structstaff
{charname[20]; /*姓名*/
chardepartment[20]; /*部門*/
intsalary; /*工資*/
intcost; /*扣款*/
intrealsum; /*實發(fā)工資*/
};
structstaffworker1,worker2;
/*定義兩個staff結(jié)構(gòu)體類型的變量*/形式二:類型、變量一起定義。
structstaff
{charname[20];
chardepartment[20];
intsalary;
intcost;
intrealsum;
}worker1,worker2;形式三:省略類型名的變量定義。
struct
{charname[20];
chardepartment[20];
intsalary;
intcost;
intrealsum;
}worker1,worker2;其中以第一種形式最好,概念清楚,易于擴充移植。定義了結(jié)構(gòu)體型變量之后,結(jié)構(gòu)體類型名的任務就完成了,在后續(xù)的程序中不再對其操作,而只對這些變量(如worker1,worker2)進行賦值、存取或運算等操作。在編譯時,結(jié)構(gòu)體類型并不分配空間,只對結(jié)構(gòu)體類型的變量分配空間。結(jié)構(gòu)體型變量所占內(nèi)存空間是各成員變量所占內(nèi)存單元的總和,一般占一片連續(xù)的存儲單元,如worker1,worker2兩個變量占內(nèi)存情況如圖9.2所示。由于worker1,worker2兩個變量都是staff這種結(jié)構(gòu)體類型,因此它們的內(nèi)存布局和組成都是一樣的,都是從staff結(jié)構(gòu)體類型的模子“印”出來的。變量的具體取值在初始化或賦值時裝入。圖9.2結(jié)構(gòu)體型變量在內(nèi)存中的存儲形式9.1.4結(jié)構(gòu)體型變量及其成員的引用定義結(jié)構(gòu)體型變量的目的是為了在后續(xù)程序中對它進行操作。但首先要注意的是結(jié)構(gòu)體型變量是一種聚合型變量??稍L問的對象有如下兩個:
·
結(jié)構(gòu)體類型的變量名本身代表變量的整體,即整個結(jié)構(gòu)體。
·成員名代表結(jié)構(gòu)體型變量的各個組成成員。上述兩個對象均可在程序中引用。
(1)變量成員的引用方法:C語言提供了訪問結(jié)構(gòu)體類型變量的成員運算符“.”(稱為點運算符),可以將結(jié)構(gòu)成員與結(jié)構(gòu)變量名聯(lián)系起來。訪問結(jié)構(gòu)成員的形式為:結(jié)構(gòu)體變量名.成員名點運算符是一個二元運算符,它處于最高優(yōu)先級,結(jié)合性為從左至右。例如,假設已經(jīng)定義了結(jié)構(gòu)體類型structstaff的兩個變量worker1、worker2,則
worker1.salary就可以訪問或引用結(jié)構(gòu)體變量worker1的成員salary,見圖9.3。圖9.3點運算符這樣,結(jié)構(gòu)成員就可以和普通變量一樣參加各種運算,且所引用的成員與其所屬類型的普通變量一樣可進行該類型所允許的任何運算。例如:
worker1.realsum=worker1.salary-worker1.cost;
worker2.salary=worker1.salary;
scanf("%s",);
scanf("%d",&worker1.cost);注意,取結(jié)構(gòu)成員的地址時,“&”要放在結(jié)構(gòu)變量名前面而不是結(jié)構(gòu)成員名前面,即不能寫成:
scanf("%d",worker1.&cost);另外,“&”和“.”同時出現(xiàn)時,由于運算符“.”的優(yōu)先級高于“&”,因此對&worker1.cost而言,是先進行“.”運算,即先取成員,然后再取該成員的地址,就是說worker1.cost與&(worker1.cost)等價。又如:“structstudentstu1,stu2;”之后,變量stu1,stu2成員的引用可以是:
stu2.num=stu2.num+1;
stu1.age++;/*等價于(stu1.age)++*/
scanf("%ld",&stu1.num);
(2)相同結(jié)構(gòu)體類型的結(jié)構(gòu)體變量可以通過整體引用來賦值:如“worker2=worker1;”即將變量worker1的所有成員的值一一對應地賦給(拷貝)變量worker2的各成員。但是,注意下列用法是錯誤的:
worker2==worker1
worker2!=worker1
C語言不允許對結(jié)構(gòu)體型變量進行任何邏輯運算。如果要對結(jié)構(gòu)體型變量進行比較,只能是逐個成員進行比較。另外,也不可以對結(jié)構(gòu)體型變量進行整體的輸入/輸出,如:
printf("%s",worker1);是錯誤的。結(jié)構(gòu)體型變量只能對每個成員逐個進行輸入或輸出。
(3)結(jié)構(gòu)體型變量占據(jù)的一片存儲單元的首地址稱為該結(jié)構(gòu)體型變量的地址,其每個成員占據(jù)的若干單元的首地址稱為該成員的地址,兩個地址均可引用。如:
scanf("%d",&worker1.salary);
printf("0x%x",&worker1);雖然兩者均是地址,但若將其賦給指針,相應的指針類型是不同的。&worker1.salary應賦給一個整形指針,而&worker1應賦給一個結(jié)構(gòu)體型指針,二者不可混用。兩指針類型也不可強制轉(zhuǎn)換。結(jié)構(gòu)體型變量的地址主要用于作為函數(shù)的參數(shù),在函數(shù)之間傳遞結(jié)構(gòu)體型數(shù)據(jù)。9.1.5結(jié)構(gòu)體型變量的初始化如前節(jié)所述,可以用讀入函數(shù)或賦值語句對結(jié)構(gòu)體型變量的成員逐個賦值,但更方便的是直接以初值表的形式賦值,稱為初始化。例如:已定義結(jié)構(gòu)體類型staff如前,則worker1的初始化:
structstaffworker1={"Wang_Wei","see",1200,100,1100};此時結(jié)構(gòu)體型變量worker1在內(nèi)存的布局如圖9.4所示。所有結(jié)構(gòu)體型變量,不管是全局變量還是局部變量,自動變量還是靜態(tài)變量均可如此初始化。圖9.4結(jié)構(gòu)體變量的成員在內(nèi)存中的布局例9.1
利用結(jié)構(gòu)體類型,編程計算一名同學5門課程的平均分。
#include<stdio.h>
voidmain()
{
structstuscore
{
charname[20];
floatscore[5];
floataverage;
};
structstuscorex={“Wang_Wei”,90.5,85,
70,90,98.5};
inti;
floatsum=0;
for(i=0;i<5;i++)
sum+=x.score[i];
x.average=sum/5;
printf(“Theaveragescoreof%sis%4.1f\n”,
,x.average);
}9.1.6應用舉例
例9.2
將例9.1改為鍵入任意多人的5門課的成績,打印平均分。
#include<stdio.h>
#include<conio.h>
voidmain()
{
structstuscore
{charname[20];
floatscore[5];
floataverage;
}x;
inti;
floatsum;
charrep;
while(1)
{
printf("\nDoyouwanttocontinue?(Y/N)");
rep=getche();
if(rep==′N′||rep==′n′)break;
sum=0;
printf("\nInputname(asXu_jun)and5scores
(alldepartbyspace);\n");
scanf(“%s”,); /*輸入名字*/
for(i=0;i<5;i++) /*輸入5門課成績*/
scanf("%f",&x.score[i]);
for(i=0;i<5;i++)
sum+=x.score[i];
x.average=sum/5; /*計算平均分*/
printf(“Theaveragescoreof%sis%4.1f\n”,
,x.average); /*打印輸出*/
}
}運行結(jié)果:
Doyouwanttocontinue?(Y/N)Y↙
Inputname(asXu_jun)and5scores(alldepartbyspace);
Guo_Yong8089.59987.566↙
TheaveragescoreofGuo_Yongis84.4
Doyouwanttocontinue?(Y/N)Y↙
Inputname(asXu_jun)and5scores(alldepartbyspace);
Liu_Ying8788899998↙
TheaveragescoreofLiu_Yingis92.2
Doyouwanttocontinue?(Y/N)N↙9.2嵌套結(jié)構(gòu)允許一個結(jié)構(gòu)體包含另一個結(jié)構(gòu)體或者把一個結(jié)構(gòu)體成員定義為數(shù)組,有時是很方便的。我們把成員之一定義為其它結(jié)構(gòu)體類型,稱為結(jié)構(gòu)體類型的嵌套(Nest)。例如我們前面定義了一個student結(jié)構(gòu)體類型,其中的age(年齡)成員可以用birthday(生日)來代替,而birthday的數(shù)據(jù)類型則是如圖9.5所定義的date結(jié)構(gòu)體類型。圖9.5結(jié)構(gòu)體類型的嵌套定義其中的成員變量birthday是結(jié)構(gòu)體類型structdate型,形成了結(jié)構(gòu)體類型的嵌套定義。另外,在student結(jié)構(gòu)體類型中,還定義了一個成員,其類型是一指向結(jié)構(gòu)體類型student自身的指針,我們稱這種結(jié)構(gòu)為遞歸結(jié)構(gòu)或自嵌套結(jié)構(gòu)。在student類型的定義中,此種指向student自身的指針成員是允許的,也是鏈表、隊列或數(shù)等復雜的數(shù)據(jù)結(jié)構(gòu)所必需的。若為自身類型的普通成員變量則是不允許的,即student結(jié)構(gòu)體類型中不可出現(xiàn)structstudent類型的非指針變量。嵌套結(jié)構(gòu)的變量定義和一般的結(jié)構(gòu)變量定義是相同的,如:
structstudentstu3;定義了一個名字為stu3的student結(jié)構(gòu)體類型的變量。同樣可以在定義變量stu3時進行初始化:
structstudentstu3={99010,"Liu_Ping",′m′,{1987,10,2}};必須注意的是,由于結(jié)構(gòu)體變量stu3的birthday成員是結(jié)構(gòu)體類型,因此該成員的初始值必須用括號括起來,而各個成員的初始值之間還是用逗號分隔。如果要訪問嵌套結(jié)構(gòu)體變量的成員,必須使用點運算符兩次(若是多級嵌套定義,則需用若干“.”運算符,逐級引用到最低級)。對student結(jié)構(gòu)體型變量stu3的成員引用如下所示,其含義見圖9.6。
stu3.birthday.year;/*其值為1987*/
stu3.birthday.month;/*其值為10*/注意,不能對stu3.birthday進行操作,因其不是最低級。我們同樣還可以把date結(jié)構(gòu)體類型的定義嵌到student結(jié)構(gòu)體類型當中,如:
structstudent
{
longintnum;
charname[20];
charsex;
structdate
{
intyear; /*年*/
intmonth; /*月*/
intday; /*日*/
}birthday;
structstudent*ps;
};這兩種結(jié)構(gòu)體類型的嵌套定義是等價的。圖9.6嵌套結(jié)構(gòu)和點運算符9.3結(jié)構(gòu)體型數(shù)組如前所述,一個結(jié)構(gòu)體型變量只能存放由該結(jié)構(gòu)體類型所定義的一條記錄。如“structstudentstu1;”中的變量stu1中存放了由結(jié)構(gòu)體類型structstudent決定的學生1的記錄,即學生的學號、姓名、性別、年齡。但正如數(shù)組概念的引出一樣,我們一般要編程處理的不只是一個學生的數(shù)據(jù)記錄,而是很多學生。這時就要用到結(jié)構(gòu)體型數(shù)組stu[50],每個數(shù)組元素stu[i]都可以存儲一個學生的記錄。結(jié)構(gòu)體型數(shù)組在構(gòu)造樹、表、隊列等數(shù)據(jù)結(jié)構(gòu)時特別方便。9.3.1結(jié)構(gòu)體型數(shù)組的定義相似于整型數(shù)組inta[3],結(jié)構(gòu)體型數(shù)組定義的不同之處是要先定義好結(jié)構(gòu)體類型。例如:
structstudent定義如前,然后“structstudentstu[3];”就定義了一個structstudent結(jié)構(gòu)體型數(shù)組stu,數(shù)組元素stu[0],stu[1],stu[2]分別是三個structstudent結(jié)構(gòu)體型變量。又如:“structstaffworker[100];”定義了另一個結(jié)構(gòu)體類型structstaff的數(shù)組worker,數(shù)組元素worker[0],worker[1],…,worker[99]分別是100個structstaff結(jié)構(gòu)體型變量。因此,定義結(jié)構(gòu)數(shù)組和定義結(jié)構(gòu)變量完全類似,也可以用直接定義、間接定義和一次性定義的方式,只要在結(jié)構(gòu)名的后面加上數(shù)組維界說明符即可。9.3.2結(jié)構(gòu)體型數(shù)組的初始化結(jié)構(gòu)體型數(shù)組和普通數(shù)組一樣可以初始化,只是每個元素的初值為由{}括起來的一組數(shù)據(jù),初始化形式是定義數(shù)組時,后面加上={初值列表},如:structstudentstu[2]={{99010,“Wang_Yan”,′f′,20},
{99011,"Li_Li",′m′,19}};
一個結(jié)構(gòu)數(shù)組元素相當于一個結(jié)構(gòu)變量。因此,訪問結(jié)構(gòu)數(shù)組元素的成員與訪問結(jié)構(gòu)變量的成員具有相同的規(guī)則。例如:
stu[i].num表示結(jié)構(gòu)數(shù)組第i+1個元素中成員num的值;
&stu[i].num表示結(jié)構(gòu)數(shù)組第i+1個元素中成員num的地址;
stu[i].name表示結(jié)構(gòu)數(shù)組第i+1個元素中成員name的首地址;
stu[i].name[j]表示結(jié)構(gòu)數(shù)組第i+1個元素中成員name第j+1個字符。因為“.”運算符優(yōu)先于“&”和“*”運算符,所以,&stu[i].num與&(stu[i].num)等價;stu[i].name[j]與*&stu[i].name[j]等價。
例9.3
用結(jié)構(gòu)體型數(shù)組初始化建立一工資登記表。然后鍵入其中一人的姓名,查詢其工資情況。
#include<string.h>
#include<stdio.h>
voidmain()
{
structstaff
{charname[20];
chardepartment[20];
intsalary;
intcost;
}worker[3]={ /*初始化*/
{"Xu_Guo","part1",800,200},
{"Wu_Xia","part2",1000,300},
{"Li_Jun","part3",1200,350}
};
inti;
charxname[20];
printf("\nInputtheworker\′sname:");
scanf(“%s”,xname);/*輸入職工名字*/
for(i=0;i<3;i++)
if(strcmp(xname,worker[i].name)==0)
/*查詢是否有此職工*/
{
printf("****%s****",xname);
printf(“\nsalary:%6d”,worker[i].
salary);
printf("\ncost:%6d",worker[i].cost);
printf(“\npayed:%6d”,worker[i].
salary-worker[i].cost);
}
}運行結(jié)果:
Inputtheworker′sname:Wu_Xia↙****Wu_Xia****
salary:1000
cost:300
payed:7009.4結(jié)構(gòu)體型指針定義一個指針,讓它存放一個結(jié)構(gòu)體型變量或結(jié)構(gòu)體型數(shù)組的首地址,它就是指向該結(jié)構(gòu)體型變量或結(jié)構(gòu)體型數(shù)組的指針,我們統(tǒng)稱之為結(jié)構(gòu)體型指針。由于結(jié)構(gòu)體類型并非全新的數(shù)據(jù)類型,它只是對以前所學的多種類型的“封裝”:將一組相關的不同類型的數(shù)據(jù)看成一個整體。所以引入結(jié)構(gòu)體型指針,可以達到:
(1)非常有效的函數(shù)參數(shù)傳遞。
(2)提高數(shù)組的訪問效率。結(jié)構(gòu)體型指針特別之處還在于:利用它可以建立動態(tài)變化的數(shù)據(jù)結(jié)構(gòu),動態(tài)地、合理地分配內(nèi)存。這一點將在下一節(jié)詳細介紹。9.4.1指向結(jié)構(gòu)體型變量的指針定義結(jié)構(gòu)體型指針和定義變量指針類似,一般形式為:[存儲類型]結(jié)構(gòu)體類型*指針1,*指針2,…;例如:
staticstructstaff*sp;
/*定義了一個靜態(tài)的structstaff結(jié)構(gòu)類型的指針sp*/
structstudent*p1;
/*定義了一個structstudent結(jié)構(gòu)類型的指針p*/
structstudentstu1,*p2;
/*定義了一個自動的student結(jié)構(gòu)體類型變量stu1和指針p2*/其中,staff和student是已經(jīng)定義過的結(jié)構(gòu)體類型名。上面定義的結(jié)構(gòu)指針只說明了指針的類型,還未確定它的指向,是一種無定向的指針,必須通過初始化或賦值,把實際存在的某個結(jié)構(gòu)體型變量或結(jié)構(gòu)體型數(shù)組的首地址賦給它以后,才可確定它的具體指向,從而使它與相應的變量或數(shù)組建立聯(lián)系。例如:
p2=&stu1;/*賦值方式*/或者
structstudent*p2=&stu1;/*初始化方式*/之后,p2才真正指向了結(jié)構(gòu)體型變量stu1,如圖9.7所示。圖9.7結(jié)構(gòu)體型指針初始化定義了一個指向指定結(jié)構(gòu)的結(jié)構(gòu)指針以后,就可以用結(jié)構(gòu)指針來訪問結(jié)構(gòu)成員。例如p2指向structstudent型結(jié)構(gòu),用(*p2).num、(*p2).name、(*p2).age等就可以訪問其成員。其中,“*”是訪問地址運算符,“.”是取成員運算符,由于“.”的優(yōu)先級高于“*”,因此(*p2).num中的圓括號就不能省略。若寫成*p2.num,則表示*(p2.num),如果num不是指針(地址量),則這種寫法就是非法的。也就是說,用指針訪問結(jié)構(gòu)成員,可用如下形式:
(*指針名).成員名這與前面介紹過的用結(jié)構(gòu)名訪問結(jié)構(gòu)成員的形式:結(jié)構(gòu)名.成員名是等效的。由于指向結(jié)構(gòu)體型的指針使用得非常頻繁,故C語言提供了另一種簡便的取結(jié)構(gòu)體成員運算符“->”,稱為指向成員運算符(或箭頭運算符)。它由一個減號和一個大于號組成。因此用指針訪問結(jié)構(gòu)成員,又可以寫成:指針名->成員名運算符“->”和“.”都是訪問結(jié)構(gòu)成員運算符,并同處于最高優(yōu)先級,其結(jié)合性也都是從左到右。假設已說明
structstudentstu1,*sp=&stu1;若要訪問其成員name,則下面三個形式等效:
(*sp).namesp->name顯然,當用結(jié)構(gòu)名訪問結(jié)構(gòu)成員時用“.”比較方便,當用指針訪問結(jié)構(gòu)成員時用“->”較為方便。注意以下表達式的含義(指向運算符->的優(yōu)先級高于++):
sp->num:得到sp指向的結(jié)構(gòu)體型變量中的成員num的值,假設其值為990120。
sp->num++:等價于((sp->num)++),得到sp指向的結(jié)構(gòu)體型變量中的成員num的值,用完該值后對它加1。
++sp->num:等價于++(sp->num),得到sp指向的結(jié)構(gòu)體型變量中的成員num的值,使之先加1,再使用。從以下用三條連續(xù)的輸出語句的執(zhí)行結(jié)果可清楚地看到其含義:
printf(“%d\n”,sp->num); 輸出990120
printf(“%d\n”,sp->num++); 輸出990120
printf(“%d\n”,++sp->num); 輸出990122現(xiàn)舉例說明結(jié)構(gòu)體型變量成員的三種引用形式。例9.4
顯示某人的工資信息。
#include<string.h>
#include<stdio.h>
voidmain()
{
structstaff
{
charname[20];
chardepartment[20];
intsalary;
};
structstaffw1,*p;
p=&w1;
strcpy(,"Li_Li");
strcpy((*p).department,"part1");
p->salary=1000;
printf(“%s%s%d\n”,,w1.department,
w1.salary);
printf(“%s%s%d\n”,(*p).name,(*p).department,
(*p).salary);
printf(“%s%s%d\n”,p->name,p->department,
p->salary);
}運行結(jié)果:
Li_Lipart11000
Li_Lipart11000
Li_Lipart11000可見,最后三行的輸出結(jié)果都是一樣的。9.4.2指向結(jié)構(gòu)體型數(shù)組的指針指向結(jié)構(gòu)體型數(shù)組的指針完全類似于第八章所述的指向普通數(shù)組的指針。一個結(jié)構(gòu)體型數(shù)組可以用結(jié)構(gòu)體型指針來訪問,既方便了數(shù)組元素的引用,又提高了數(shù)組的利用率。
例9.5
顯示工資表。
#include<stdio.h>
structstaff
{
charname[20];
intsalary;
};
voidmain()
{
structstaff*p;
structstaffworker[3]={{"Wang_Li",600},
{"Li_Ping",700},
{"Liu_Yuan",800}};
for(p=worker;p<worker+3;p++)printf(“%s\′ssalaryis%dyuan\n”,
p->name,p->salary);
}運行結(jié)果:
Wang_Li′ssalaryis600yuan
Li_Ping′ssalaryis700yuan
Liu_Yuan′ssalaryis800yuan其中,p是指向structstaff結(jié)構(gòu)體類型數(shù)據(jù)的指針。for語句中p=worker使指針p實際地指向了數(shù)組worker,即p中存放的是數(shù)組worker的起始地址,等價于p=&worker[0],如圖9.8所示。在第一次循環(huán)中,程序輸出了worker[0]的各成員,然后執(zhí)行p++,進行第二次循環(huán)。如前所述,C語言中的指針加1,并不是實際的內(nèi)存地址值加1,而是指向了下一個數(shù)據(jù),對于結(jié)構(gòu)體型數(shù)組,則指向了下一個元素,即相當于p=&worker[1](即圖中p′),這樣循環(huán)體內(nèi)的printf語句輸出的便是worker[1]的各成員。以此類推,在輸出了worker[2]之后p的值加到了worker+3,便不滿足循環(huán)條件p<worker+3了,程序結(jié)束。圖9.8指針與結(jié)構(gòu)體型數(shù)組對于指向數(shù)組的指針,用得更多的是指針值本身的加1移動,所以要特別注意:
(++p)->salary:先使p自加1,指向下一元素,得到worker[1].salary的值,即700。
p++->salary:先得到worker[0].salary的值,即600,然后使p加1,指向worker[1]。
(p++)->salary:完全同上。括號不改變++操作符以后的操作性質(zhì)。如在例9.5的worker數(shù)組初始化,且p=worker之后,連續(xù)執(zhí)行以下四條語句,輸出結(jié)果為:
printf(“%d\n”,p->salary); 輸出600
printf(“%d\n”,(++p)->salary); 輸出700
printf(“%d\n”,p++->salary); 輸出700
printf(“%d\n”,(p++)->salary); 輸出8009.5結(jié)構(gòu)體與函數(shù)結(jié)構(gòu)體與函數(shù)之間的關系主要是:
·結(jié)構(gòu)體作為函數(shù)參數(shù)。此時,結(jié)構(gòu)體型變量、結(jié)構(gòu)體型變量的成員和結(jié)構(gòu)體指針都可以作為函數(shù)的參數(shù)進行傳遞。
·
結(jié)構(gòu)體作為函數(shù)的返回值。9.5.1結(jié)構(gòu)體作為函數(shù)參數(shù)在編程處理結(jié)構(gòu)體型數(shù)據(jù)時,常常需要將一個結(jié)構(gòu)體型變量的值或一個結(jié)構(gòu)體型數(shù)組傳遞給另一個函數(shù),此時,同樣遵循第七章中有關值傳遞和地址傳遞的規(guī)律?!爸祩鬟f”的一種是將結(jié)構(gòu)體型變量的各個成員作為實際參數(shù),這和普通變量作實參的含義是一樣的,形式參數(shù)為相應類型的普通變量。例9.6
用子函數(shù)求出worker數(shù)組中每個工人的實發(fā)工資。
/*傳遞結(jié)構(gòu)體型變量的成員*/
#include<stdio.h>
structstaff
{
charname[20];
chardepartment[20];
intsalary;
intcost;
intrealsum;
}worker[3]={
{"Wang_Li","part1",1000,200},
{"Li_Ping","part2",1500,300},
{"Liu_Yuan","part3",2000,600}};
intgetreal(intsalary,intcost);/*函數(shù)聲明,注意其形參*/
voidmain()
{
structstaff*p;
intrealsum;
for(p=worker;p<worker+3;p++)
{
realsum=getreal(p->salary,p->cost);
/*函數(shù)調(diào)用,計算實發(fā)工資*/
printf(“%sof%sshouldbepayed%dyuan\n”,
p->name,p->department,realsum);
}
}
intgetreal(intsalary,intcost)
/*函數(shù)實現(xiàn)*/
{
returnsalary-cost;
}運行結(jié)果:
Wang_Liofpart1shouldbepayed800yuan
Li_Pingofpart2shouldbepayed1200yuan
Liu_Yuanofpart3shouldbepayed1400yuan此例中子函數(shù)完成的任務很簡單,只是為了說明主、子函數(shù)間形、實參是如何傳遞的。數(shù)組名worker代表了數(shù)組worker的首地址。注意,函數(shù)getreal的兩個參數(shù)是一般的整數(shù)類型,它既不知道,也不關心實參究竟是不是結(jié)構(gòu)的成員,它只要求它們是int類型?!爸祩鬟f”的另一種是用結(jié)構(gòu)體型變量作實參,將結(jié)構(gòu)體變量所占的內(nèi)存單元的內(nèi)容全部順序傳遞給形參。形參也必須是同類型的結(jié)構(gòu)體型變量。這兩種傳遞都是單向的,子函數(shù)對形參的處理改變不能影響作為實參的結(jié)構(gòu)體型變量或者其成員的值,即處理結(jié)果都是帶不回來的。對于結(jié)構(gòu)體型數(shù)據(jù),數(shù)值傳遞法的不恰當之處是當結(jié)構(gòu)體型變量的成員很多,或有一些成員是數(shù)組時,數(shù)據(jù)量很大,將全部成員一個個傳遞,既浪費時間又浪費空間,程序的運行效率會大大降低。所以,用結(jié)構(gòu)體型指針作函數(shù)參數(shù),即地址傳遞效果就比較好。例9.7
用子函數(shù)求出worker數(shù)組中每個工人的實發(fā)工資。
/*傳遞結(jié)構(gòu)體型變量*/
#include<stdio.h>
structstaff
{
charname[20];
chardepartment[20];
intsalary;
intcost;
intrealsum;
}worker[3]={
{"Wang_Li","part1",1000,200},
{"Li_Ping","part2",1500,300},
{"Liu_Yuan","part3",2000,600}};
intgetreal(structstaff);
voidmain()
{
structstaff*p;
intrealsum;
for(p=worker;p<worker+3;p++)
{
realsum=getreal((*p));/**p表示數(shù)組元素*/
printf("%sof%sshouldbepayed%dyuan\n“,
p->name,p->department,
realsum);
}
}
intgetreal(structstaffss)/*結(jié)構(gòu)體型變量作為形參*/
{
returnss.salary-ss.cost;
}運行結(jié)果同上。“地址傳遞”即實際參數(shù)為具體的結(jié)構(gòu)體型變量的地址或者結(jié)構(gòu)體型數(shù)組名,將結(jié)構(gòu)體型變量或數(shù)組的地址傳給形參,形參為結(jié)構(gòu)體型指針或結(jié)構(gòu)體型數(shù)組名。這種傳遞是“雙向”的。實際上是實參、形參指向了同一片存儲空間,不需要開辟大量的存放數(shù)據(jù)的空間,更不需要費時間傳遞大量的數(shù)據(jù),且被調(diào)函數(shù)對該區(qū)的處理結(jié)果是可以“返回”給主調(diào)函數(shù)的。這已經(jīng)在第七章詳細闡述了,此處再以結(jié)構(gòu)體型數(shù)據(jù)為例來說明之。例9.8
用子函數(shù)求出worker數(shù)組中每個工人的實發(fā)工資。
/*傳遞結(jié)構(gòu)體類型指針*/
#include<stdio.h>
#defineNUM3
structstaff
{
charname[20];
chardepartment[20];
intsalary;
intcost;
intrealsum;
}worker[NUM]={
{"Wang_Li","part1",1000,200},
{"Li_Ping","part2",1500,300},
{"Liu_Yuan","part3",2000,600}};
voidgetreal(structstaff*);
voidmain()
{
structstaff*p;
for(p=worker;p<worker+NUM;p++)
{
getreal(p);
printf("%sof%sshouldbepayed%dyuan\n“,
p->name,p->department,p->realsum);
}
}
voidgetreal(structstaff*ps)
/*用結(jié)構(gòu)體指針作為形參*/
{
ps->realsum=ps->salary-ps->cost;
/*雙向傳遞,處理結(jié)果*/
}運行結(jié)果同上。9.5.2結(jié)構(gòu)體作為函數(shù)的返回值由于數(shù)組和結(jié)構(gòu)體類型都屬于數(shù)據(jù)結(jié)構(gòu),有些讀者可能會認為,C語言對它們的處理應該是相似的。事實上,學習C語言的時候一定要摒棄這種想法,因為C語言處理結(jié)構(gòu)體類型的方法與處理簡單數(shù)據(jù)類型的方法相似,但絕對與處理數(shù)組的方法不同。函數(shù)在返回數(shù)組時,無法返回整個數(shù)組的值,只能返回某個元素或者元素的地址。而函數(shù)在處理結(jié)構(gòu)體類型時,可以將其模塊化為簡單類型來計算結(jié)果。即使結(jié)構(gòu)體包含數(shù)組,函數(shù)也不是返回數(shù)組的首地址,而是直接返回結(jié)構(gòu)體所有成員的值。在例9.9中,模擬了時間在某個特定周期之后進行更新的情形。假設使用的是24小時的時鐘。例中new_time()函數(shù)可以根據(jù)原始時間和自前一次更新開始時間流逝的秒數(shù),返回本次的更新時間值。如果time_now是21:58:32,secs的值為97,那么通過調(diào)用new_time(time_now,secs),執(zhí)行的返回結(jié)果就應該是22:00:09。整個調(diào)用過程如圖9.9所示。圖9.9結(jié)構(gòu)體類型值作為函數(shù)的輸入?yún)?shù)和返回結(jié)果
例9.9
模擬時間更新程序。
#include<stdio.h>
structtime{
/*時間結(jié)構(gòu)體*/
inthour,minute,second;/*小時:分:秒*/
};
structtimenew_time(structtime,int);
/*更新函數(shù),返回為time結(jié)構(gòu)體*/
voidmain()
{
structtimet1,t2={21,58,32};
intsecs=97;
t1=new_time(t2,secs);
printf(“newtime:%d:%d:%d\n”,t1.hour,
t1.minute,t1.second);
}
structtimenew_time(structtimett,/*當前時間*/
intelapsed_secs)/*已經(jīng)過去的時間(秒數(shù))*/
{
intnew_hour,new_min,new_sec;
new_sec=tt.second+elapsed_secs;
tt.second=new_sec%60; /*更新秒*/
new_min=tt.minute+new_sec/60;
tt.minute=new_min%60; /*更新分*/
new_hour=tt.hour+new_min/60;
tt.hour=new_hour%24; /*更新小時*/
return(tt);
}9.6內(nèi)存的動態(tài)分配9.6.1動態(tài)分配內(nèi)存的意義隨著計算機的發(fā)展,軟件的功能越來越強,相應地,軟件的規(guī)模就越來越大,而計算機內(nèi)存就成了寶貴的資源,所以我們在編制復雜程序時就要考慮盡量節(jié)省內(nèi)存。在上節(jié)的例9.8中,用了符號常量NUM來代表要處理的人數(shù),人數(shù)不同時只需改動第一行。但若編制的是應用軟件,提供給用戶的應是可執(zhí)行文件,無法根據(jù)具體情況來改變NUM的值。這時,NUM設成多少合適呢?當然,可以根據(jù)軟件使用者來定,比如:100人的車間,設計成#defineNUM100;1000人的工廠,設計成#defineNUM1000。但對于通用的商業(yè)軟件,無法預知使用者的情況。若為了程序的通用性,按內(nèi)存的允許設置成一個很大的數(shù)組,那么對于很多只處理少量信息的用戶就造成了內(nèi)存的極大浪費,這是非常不合理的。這時,能夠根據(jù)程序運行后的實際情況動態(tài)地分配適量的內(nèi)存空間就顯得非常重要!9.6.2開辟和釋放內(nèi)存區(qū)的函數(shù)為了實現(xiàn)內(nèi)存的動態(tài)分配,C語言提供了一些程序執(zhí)行后才開辟和釋放某些內(nèi)存區(qū)的函數(shù)。
1.malloc()函數(shù)它的函數(shù)原型為:
void*malloc(unsignedsize);其功能是在內(nèi)存的動態(tài)存儲區(qū)中分配長度為size個字節(jié)的連續(xù)空間。
2.free(p)函數(shù)該函數(shù)表示釋放由p指向的內(nèi)存區(qū),使這部分內(nèi)存可以分配給其它變量。下面以兩個例子來說明上述兩函數(shù)的用法。
例9.10
分配一個能放置雙精度數(shù)的空間。
#include<stdlib.h>
main()
{
double*p;
p=(double*)malloc(sizeof(double));/*注1*/
if(p==0)
{
printf("mallocerror\n");
exit(0);
}*p=78.786;
printf("*p=%f\n",*p);
}運行結(jié)果:*p=78.786000此例中,存雙精度數(shù)的空間不是在程序編譯時分配的,而是通過調(diào)用malloc()函數(shù)在程序執(zhí)行時才分配的。另外,對注1行有兩點說明:
(1)從malloc()函數(shù)原型可以得知,其返回值為void*型,現(xiàn)在是對雙精度型分配空間,所以要將其返回值強行轉(zhuǎn)換為double*型。
(2)程序中出于易于移植的考慮,使用了sizeof(double)作為malloc()函數(shù)的實參。因為不同機器上的雙精度所占的字節(jié)數(shù)可能不同,用這種方法不論在哪種機器上都能為雙精度型數(shù)據(jù)分配大小正確的空間。例9.11
改進上例,在使用后釋放動態(tài)分配的空間。
#include<stdlib.h>
main()
{
double*p,*q;
p=(double*)malloc(sizeof(double));
if(p==0){
printf("mallocerror\n");
exit(0);
}
printf("p=0x%x*p=%4.1f\n",p,*p=100);
free(p);
q=(double*)malloc(sizeof(double));
if(q==0){
printf("mallocerror\n");
exit(0);
}
*q=10.;
printf("q=0x%x*q=%4.1fp=0x%x*p=%4.1f\n",
q,*q,p,*p);
}運行結(jié)果:
p=0X4E7*p=100.0
q=0X4E7*q=10.0p=0X4E7*p=10.0指針p、q均為相同的地址值(具體值可能不是0X4E7),表明已經(jīng)釋放的由指針p所指的空間又重新分配給了指針q。由于指針p的內(nèi)容沒變,故指針p、q都指向同一空間。從第二行的結(jié)果可驗證之。在多次調(diào)用malloc()函數(shù)開辟內(nèi)存空間的過程中,可能有另一種動態(tài)變化的數(shù)據(jù)也要在此分配空間,或者前邊已分配的某個空間已被釋放,又可重新被分配。因此多次開辟的內(nèi)存空間的地址是不連續(xù)的。這一點與數(shù)組完全不同。另外兩個相關函數(shù)是calloc()及realloc(),其原型分別為:
void*calloc(unsignedn,unsignedsize);
void*realloc(void*p,unsignedsize);
calloc()的功能是分配n個大小為size個字節(jié)的連續(xù)空間,它實際上是用于動態(tài)數(shù)組的分配。
realloc()的功能是將p所指出的已分配的內(nèi)存空間重新分配成大小為size個字節(jié)的空間。它用于改變已分配的空間的大小,可以增減單元數(shù)。9.6.3鏈表概述我們在9.6.1節(jié)中所提出的問題,用上面介紹的幾個函數(shù),可以這樣解決:程序運行前不開辟任何存儲空間,在程序執(zhí)行之后,第一步調(diào)用malloc()函數(shù),例如,(structstaff*)
malloc(sizeof(structstaff))開辟一個structstaff結(jié)構(gòu)體型變量所需的內(nèi)存空間;第二步讀入一個工人的數(shù)據(jù)信息。然后重復執(zhí)行這兩步,可以開辟任意多個structstaff型變量的內(nèi)存空間,讀入相應個工人的數(shù)據(jù)信息,形成若干個內(nèi)存塊,使內(nèi)存的分配成為動態(tài)的。如前節(jié)所述,這些用malloc()函數(shù)開辟的內(nèi)存塊一般是不連續(xù)的。現(xiàn)在的問題是如何來組織管理這些不連續(xù)的內(nèi)存塊?比如說想將多個工人的工資打印出來,過去用數(shù)組靜態(tài)分配空間時,做法是將第一人信息所占空間的首地址給p,輸出p->salary之后,將p加1再輸出下一人的,但現(xiàn)在各個空間是不連續(xù)的,p加1就不一定是下一人的信息所占空間的首地址。這個地址是隨機的,必須有一指針專門記錄下來,才能把兩人的內(nèi)存塊聯(lián)系起來。所以必須在structstaff類型定義中加一個指向該結(jié)構(gòu)體類型的指針,專門用來記錄下一個內(nèi)存塊的首地址。即
structstaff{charname[20];
intsalary;
structstaff*next;
};其中的指針next將存放下一個內(nèi)存塊的首地址,也是專門用于連接兩個內(nèi)存塊的指針。像這樣的一個結(jié)構(gòu)體型變量可用來形成鏈表。每個該結(jié)構(gòu)體型數(shù)據(jù)稱為鏈表的一個結(jié)點,結(jié)點必須包含數(shù)值信息和下一個結(jié)點地址兩個部分,缺一不可。由前一個結(jié)點的指針成員指向下一個結(jié)點,可將若干個結(jié)點串接在一起,就構(gòu)成了鏈表。鏈表是將分散在內(nèi)存中的相關信息塊通過指針鏈接在一起的一種數(shù)據(jù)結(jié)構(gòu),是一種重要的常見數(shù)據(jù)結(jié)構(gòu)。利用這種數(shù)據(jù)結(jié)構(gòu)可以動態(tài)地分配內(nèi)存空間。鏈表有單向、雙向、環(huán)形等多種,我們只以最簡單的單向鏈表為例來介紹,如圖9.10所示。
head稱為“頭指針”,它應該是與結(jié)點類型相同的結(jié)構(gòu)體型指針,其中存放一個地址,該地址為鏈表中第一個結(jié)點的首地址。圖9.10單向鏈表示意圖
A、B、C、D、…、N為各結(jié)點的實際數(shù)據(jù)信息,其具體成員的個數(shù)和內(nèi)容由實際情況而定;每個結(jié)點的最后一個成員next為結(jié)點類型的結(jié)構(gòu)體型指針,存放下一個結(jié)點的首地址,即指向下一個結(jié)點。這些指針逐個指向下一個結(jié)點,并將這些地址不連續(xù)的結(jié)點“串”在一起,形成了鏈表。鏈表的長短可以是內(nèi)存允許范圍內(nèi)的任意多個。
NULL為表尾標志,單向鏈表由head指向第一個結(jié)點,第一個結(jié)點的next成員又指向第二個結(jié)點,直到最后一個結(jié)點。該結(jié)點不再指向其它結(jié)點,稱為“表尾”,它的地址部分即存放一個NULL(“表示空地址”),標志鏈表到此結(jié)束。有了以上的知識,便可以進行鏈表的處理了。9.6.4建立鏈表所謂建立鏈表即是從無到有的形成一個鏈表。建立鏈表的思想很簡單:逐個地輸入各結(jié)點的數(shù)據(jù),同時建立起各結(jié)點的關系。這種建立關系可能是正掛、倒掛或插入,下面介紹前兩種。建立鏈表方法一:正掛——先建立鏈頭,讓鏈頭指向首先開辟并輸入數(shù)據(jù)的第一個結(jié)點;然后開辟并輸入第二個結(jié)點數(shù)據(jù),將其“掛”到第一個結(jié)點之后;接著開辟第三個結(jié)點并輸入實際數(shù)據(jù),將其“掛”在第二個結(jié)點之后……即按輸入順序?qū)⒔Y(jié)點“掛”接在一起。在實際編程時,還有些細節(jié)要考慮,如是否為空鏈等。針對9.6.1節(jié)提出的問題,我們建立一個職工工資鏈表,現(xiàn)定義結(jié)點類型如下:
structstaff
{
charname[20];
intsalary;
structstaff*next;
};
(為了簡化程序,減少了原有的數(shù)據(jù)成員項)在形成鏈表的過程中,首先要定義頭指針,并且還需要兩個工作指針p1、p2,其定義如下:
structstaff*head,*p1,*p2;p1用于指向新開辟的結(jié)點,p2用于指向建鏈過程中已建鏈表的最后一個結(jié)點。首先考慮算法如圖9.11所示。圖9.11正向建立鏈表子函數(shù)流程圖具體步驟描述如下:
(1)開始時先建一個空鏈表:head=NULL;形如:。
(2)開辟第一個結(jié)點空間并由p1指向,即“p1=(structstaff*)(malloc(LEN));”,LEN為結(jié)點結(jié)構(gòu)體類型staff的一個變量所占字節(jié)數(shù)。之后,執(zhí)行語句:
scanf("%s%d",p1->name,&p1->salary);讀入其有效數(shù)據(jù)(以工資大于0為有效數(shù)據(jù)),執(zhí)行“head=p1;”,將其掛到鏈頭上(如虛線所示,其后的鏈表圖類似)。HeadNULL形如:其中worker1代表第一個工人的姓名;至于head的具體內(nèi)容是什么,即p1的值是多少,由系統(tǒng)決定,我們無需關心。
(3)移動p2,使其指向最后一個結(jié)點,即執(zhí)行p2=p1。形如:
(4)再開辟下一個結(jié)點的空間由p1指向,即再次執(zhí)行:
p1=(structstaff*)malloc(LEN);讀入有效數(shù)據(jù)后,執(zhí)行“p2->next=p1;”,將其掛至鏈尾。
(5)重復(3)、(4)兩步,直至所讀數(shù)據(jù)無效,即p2所指為真正尾結(jié)點,此時令p2->next=NULL,建鏈結(jié)束。形如:相應程序如下(附一個遍歷顯示子函數(shù)print(),以便查看建鏈后鏈表內(nèi)各結(jié)點的情況)。
例9.12
正向建立鏈表程序清單。
#include<stdlib.h>
#defineNULL0
#defineLENsizeof(structstaff)
structstaff
{
charname[20];
intsalary;
structstaff*next;
};
intn;
main()
{
structstaff*creat1(); /*二函數(shù)聲明*/
voidprint(structstaff*p);
structstaff*head;
head=creat1(); /*調(diào)子函數(shù)建立鏈表*/
print(head);
/*從頭開始顯示鏈表各結(jié)點的數(shù)據(jù)信息*/
}
structstaff*creat1()
{
structstaff*head,*p1,*p2;
n=0;
p1=(structstaff*)malloc(LEN);/*開辟第一結(jié)點*/
printf("Inputtheworker\′snamesalary(salary=0
end):\n");
scanf(“%s%d”,p1->name,&p1->salary);
/*讀入第一結(jié)點數(shù)據(jù)*/
head=NULL;/*建空鏈*/
while(p1->salary>0)
{
n=n+1; /*結(jié)點數(shù)加1*/if(n==1)head=p1; /*“掛鏈”*/elsep2->next=p1;
p2=p1; /*移動指針p2*/p1=(structstaff*)malloc(LEN);/*開辟下一結(jié)點空間*/scanf(“%s%d”,p1->name,&p1->salary);
/*讀入數(shù)據(jù)*/}
p2->next=NULL; /*數(shù)據(jù)無效置鏈尾*/
return(head); /*返回鏈頭*/
}
voidprint(structstaff*head)
{
structstaff*p;
p=head; /*p指向鏈頭*/
while(p!=NULL)
/*未至鏈尾,則顯示結(jié)點數(shù)據(jù)信息*/{
printf("%s\′ssalaryis%d\n“,p->name,p->salary);
p=p->next; /*p后移一結(jié)點*/}}其中定義的建鏈函數(shù)creat1()的返回值為structstaff結(jié)構(gòu)體型指針,由它帶回所建鏈表的起始地址(即return(head)中的head——頭指針);n為結(jié)點個數(shù)。程序運行情況:
Inputtheworker′snamesalary(salary=0end):
W11000↙ (輸入)
W22000↙
W33000↙
W0↙
W1′ssalaryis1000
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024年UPS產(chǎn)品保修及售后服務條款2篇
- 2024年版加油服務全面承包協(xié)議模板版B版
- 2024-2030年中國實時數(shù)據(jù)庫行業(yè)發(fā)展模式規(guī)劃分析報告
- 2024-2030年中國城市配送行業(yè)發(fā)展模式規(guī)劃分析報告
- 2024年獨家版:新材料研發(fā)與技術轉(zhuǎn)讓合同
- 2024年物業(yè)管理與保養(yǎng)服務合同書版B版
- 2024年技術服務與維護合同
- 2024年挖掘機租賃期間的保險責任合同
- 2025個人承包快遞運輸合同
- 單位人力資源管理制度展示大全
- FMEA-培訓教材-汽車fmea培訓課件
- 《項目進度管理研究文獻綜述》
- 信用風險加權資產(chǎn)計量與管理手冊課件
- 光伏項目試驗報告
- 小學“雙減”作業(yè)設計:小學數(shù)學四年級上冊作業(yè)設計案例
- 知識產(chǎn)權法(英文) Intellectual Property Right Law課件
- 綜合評分法評分表(建設工程)
- SBS卷材防水施工工藝
- 深化設計確認記錄
- 小學生心理健康教育課件
- 熱力管道焊接技術交底記錄大全
評論
0/150
提交評論