《C语言项目开发教程PPT第6章.ppt》由会员分享,可在线阅读,更多相关《C语言项目开发教程PPT第6章.ppt(177页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第6章 C语言的数据类型有基本数据类型和构造数据类型。基本数据类型提供了对单一数据的表达方式,利用数组可以表示由同类型的多个元素构成的复合对象,即把有限个相同类型的数据作为一个变量进行整体操作。但是在实际应用中,存在着很多更为复杂的对象,需要由多种不同类型的属性来描述,例如一个学生的信息包括学生学号、姓名、年龄、成绩等属性,且每一个属性的数据类型不同,这些不同的类型的数据共同构成一个整体学生,而如果每一个属性都用简单的变量来分别表示,则程序会变得比较松散、复杂,数据难以处理。为了解决类似这样的问题,C语言中允许用户自己定义一种数据类型,以整体的形式来描述多个属性,即构造数据类型。构造数据类型又
2、分为三种:结构体、共用体和枚举。应用这三种复杂数据类型,可以很容易地描述和构造不同的数据结构,并对这些数据进行比较方便的操作。任务6.1学生成绩排序结构体任务目标了解结构体的概念和使用范围。掌握结构体的声明、变量定义、初始化和引用方法。掌握结构体数组的使用方法。掌握结构体指针的使用方法。掌握结构体作为函数参数时的用法。掌握结构体类型函数的用法。用结构体实现学生成绩排序任务。6.1.1结构体1结构体的声明程序的目的是解决客观世界中存在的问题,因此常常需要模拟客观世界中的事物或者概念,如学生。在计算机中是如何表示一个学生或者其他事物的呢?首先要做的是找到这个事物中被关注的信息。比如在学生信息管理系
3、统中,被关注的是学生的学号、姓名、年龄、性别、院系等信息,则在计算机中将这些信息表示出来,即可代表一个学生。但是不同的属性具有不同的数据类型,如学号为整型或者字符型、姓名为字符型、性别为字符型、学院为字符型、成绩为浮点型等,如表6-1所示。表6-1学生信息表学号(整型)姓名(字符串)出生日期(整型)性别(字符型,m为男,f为女)院系(字符串)20120601李雷19880423男(0)计算机学院20120602韩梅梅 19890512女(1)计算机学院20120603张红19890810女(1)计算机学院在之前的程序中,往往是用多个变量数组来分别表示学生的各个属性,但是这每一个变量之间显示不出
4、关联性,也缺乏概念的整体性。如要查找一个学生的信息,则需要在多个变量数组中查询;要对学生进行排序,就必须对每一个变量数组都进行同样的元素交换。而这些信息也无法用二维数组来存放,因为二维数组中每一个元素的类型和长度都必须相同。那么能否将这些属性作为一个完整的类型存放呢?C语言中规定了一种新的数据类型,即结构体,来表示类型不同但是可以作为一个整体的逻辑相关的多个变量。结构体(structures)是一种由其他数据类型组合而成的新的数据类型。结构体具有组合的形式,也包含了分析的内涵,是建立在对实际数据对象分析的基础上,利用已有的数据类型重新组建而成的用户自定义数据类型。结构体在使用之前必须对其类型名
5、和数据类型进行声明,也就是声明结构体类型的名称以及构成它的每一个成员的名称和类型。结构体声明的一般形式如下:struct结构体名数据类型成员1;数据类型成员2;数据类型成员n;关键字struct表示定义的类型为结构体类型。在结构体中,可以声明若干个成员,每一个成员都是该结构体的一个组成部分。对每一个成员都要做单独的类型说明,每个成员的数据类型都可以不同。成员列表不可以为空。注意,大括号后面的分号是不可省略的,表明结构体声明的结束。例如,定义一个学生的结构体:structstudentintno;/学号charname20;/姓名intbirthday;/出生日期charsex;/性别charm
6、ajor20;/院系;在C语言中,结构体的声明是可以嵌套的。例如对于学生的出生日期,可以用整型数据来表示,也可以将日期中的年、月、日分开,用结构体来表示,例如:structdataintyear;intmonth;intday;则此时student结构体可以定义为:structstudentintno;charname20;structdatabirthday;charsex;charmajor20;其中birthday的类型为结构体data,同时又是结构体student中的一个成员,构成结构体的嵌套声明。一般把结构体的声明放在文件的最前面,也可以放在头文件内。若在函数内部声明一个结构体,在函
7、数外则无法使用这个结构体。一个结构体中的成员是另外一个结构体类型时,必须将使用的结构体的声明放在前面。同一个结构体中的成员是不可以重复的,但是不同结构体的成员可以重复。结构体成员和其他变量可以重名,它们代表不同的含义,互不干扰。2结构体变量的定义结构体类型是一个数据类型,它和C语言中的其他数据类型int、float、char等类型的作用相同。声明一个结构类型时,实际上只是指明了该数据类型的名称和成员名,是对数据类型的一种抽象的说明,其作用为规定了该类数据类型的性质与该类数据类型所占内存的大小,但此时并不为结构体分配内存空间。通过结构体类型,可以定义结构体类型变量。只有通过定义结构体变量,编译器
8、才会为其分配存储空间,才能存储真正的数据。可以这样理解结构体和结构体变量之间的关系:结构体声明时,相当于设计了一个二维表格,确定这个表格的名称,并规定好这个表格中的每一列的名称和含义,但只是设计过程;而定义一个结构体变量时,相当于将这个已设计好的二维表格在纸上画出来,并且通过变量的初始化或者赋值等可以向表格中填充数据。结构体变量的定义有3种形式。(1)先声明结构体,再定义结构体变量。例如:structstudentintno;charname20;intbirthday;charsex;structstudentstu1,stu2;上述定义中,先声明结构体student,然后定义了两个stud
9、ent类型的变量,分别为stu1和stu2。注意,在structstudent作为一个整体,表示一个student的结构体类型时,不能只用student来定义变量。可以定义宏来表示一个结构体类型。例如:#defineSTUstructstudentSTU;STUstu1,stu2;(2)类型声明的同时定义变量。这种方式在声明结构体类型的同时紧跟着定义该类型的变量,即结构体的声明和结构体变量的定义进行合并,例如:structstudentintno;charname20;intbirthday;charsex;stu1,stu2;此时大括号后面没有分号,而是在变量定义后面加分号。(3)直接定义结
10、构体变量。例如:structintno;charname20;intbirthday;charsex;stu1,stu2;这种定义方式不出现结构体名,而直接给出结构变量。此时对该结构体只能有这一次变量定义,之后不能再定义这个结构体的其他变量和指向该结构体变量的指针。结构体变量和普通变量类型,也有作用域和存储类型,即也可以为全局变量或者局部变量,可以进行静态存储和动态存储等,并且是由其定义位置的方式决定的。为结构体分配存储空间时,需要根据其每一个成员数据类型分别为每一个成员分配相应的存储空间,分配时按成员定义的顺序进行。一个结构体变量所占用的存储空间是连续的,且这片连续的存储空间的长度和其所有成
11、员所占存储空间长度之和相等。3结构体变量的初始化和其他变量类似,结构体变量可以在定义的时候进行初始化赋值,其一般形式为:struct结构体类型名结构体变量名=初值表;例如:structdataintyear;intmonth;intday;structdatanew_year=2012,0,0;初始值表中的值应和结构体定义中的成员的名称和类型相符,且结构体变量定义好之后,就不能再对各个成员的值进行整体的赋值了,例如:structdatasummer;summer=2012,07,20;上述赋值方式是错误的。和数组初始化的性质相同,结构体变量的初始化仅限于外部的和static类型的结构体。即在函
12、数内部如果想对结构体变量进行初始化,则该变量必须定义为static类型,否则将会有错误。结构体变量的初始化不允许对部分成员进行初始化。4结构体变量的引用除了初始化之外,不能对结构体变量整体进行操作,对结构体变量的赋值、输入、输出和其他操作都是通过引用结构体变量的成员来实现的。结构体成员引用的一般形式为:结构体变量名.成员名其中“.”为成员运算符,和其他操作符相比,其优先级最高。例如:structdataday1;day1.year=2012;day1.month=12;day1.day=21;【案例6-1】结构体变量的输出。#includestructdataintyear;intmonth;
13、intday;structstudentintno;charname20;structdatabirthday;charsex;charmajor20;#defineSTUstructstudentvoidmain()STUboy=20120601,LiLei,1988,12,3,m,computer;STUgirl=20120602,HanMeimei,1989,8,10,f,computer;printf(student1:n);printf(NO:%d,name:%s,birthday:%d.%d.%d,sex:%c,major:%sn,boy.no,boy.name,boy.birth
14、day.year,boy.birthday.month,boy.birthday.day,boy.sex,boy.major);printf(student2:n);printf(NO:%d,name:%s,birthday:%d.%d.%d,sex:%c,major:%sn,girl.no,girl.name,girl.birthday.year,girl.birthday.month,girl.birthday.day,girl.sex,girl.major);程序运行结果如下:student1:NO:20120601,name:LiLei,birthday:1988.12.3,sex:m
15、,major:computerstudent2:NO:20120602,name:HanMeimei,birthday:1989.8.10,sex:f,major:computer注意,student中的成员birthday仍然是一个结构体,要输出其值还需要访问结构体data中的成员。【案例6-2】判定二维平面中的三点能否构成三角形。#include#includestructpointfloatx;/横坐标floaty;/纵坐标;/求两点间距离floatlength(floatx1,floaty1,floatx2,floaty2)floatlen;len=sqrt(x1-x2)*(x1-x2
16、)+(y1-y2)*(y1-y2);returnlen;voidmain()structpointp1,p2,p3;floatlen1,len2,len3;printf(请分别输入三点坐标:n);scanf(%f,%f,&p1.x,&p1.y);scanf(%f,%f,&p2.x,&p2.y);scanf(%f,%f,&p3.x,&p3.y);len1=length(p1.x,p1.y,p2.x,p2.y);len2=length(p2.x,p2.y,p3.x,p3.y);len3=length(p3.x,p3.y,p1.x,p1.y);if(len1+len2=len3|len2+len3=
17、len1|len1+len3=len2)printf(三点不能构成三角形n);elseprintf(三点可以构成三角形n);程序运行结果如下:请分别输入三点坐标:2,00,20,0三点可以构成三角形6.1.2结构体数组与指针1结构体数组数组的元素也可以是结构体类型的,因此可以构成结构体数组。结构体数组的每一个元素都是具有相同类型的结构体变量,每一个下标元素都含有结构体类型的所有成员。在表示一个学生的信息时,可以使用一个结构体变量来完成,但是在处理多个学生信息时,则不适合用多个结构体变量来解决。单个的结构体变量的适用范围不大,一般需要建立结构体数组来表示具有相同数据结构的一个群体,如一个班级内的
18、所有学生信息,所有学生的成绩信息等。结构体数组的定义形式和结构体变量的定义形式类似,只是定义的不是单独变量,需要说明为数组类型即可。结构体数组的一般定义形式为:结构体类型数组名常量表达式;例如:structpeaplecharname20;intage;charsex;charprofession20;structpeaplestudent10;也可以在定义结构体的同时定义结构体数组或者直接定义结构体数组,而不给出结构体数组的类型名称。例如:structpeaplecharname20;intage;charsex;charprofession20;student10;与普通数组一样,只能对全
19、局和静态的结构体数组初始化,初始化的方法也与普通数组相类似。可以在定义结构体数组的同时进行初始化。例如:structstudent3=lilly,20,f,studentJack,21,m,studentLucy,21,f,student;在对所有元素进行初始化时可以不指定数组的长度,例如:structstudent=lilly,20,f,studentJack,21,m,studentLucy,21,f,student;结构体数组的存储和一般数组元素的存储相同,也是按下标连续存储的。在引用结构数组变量时,先通过下标引用结构体数组中的某一元素,再引用结构体中的某一成员。例如:student2.
20、age=22;【案例6-3】结构体数组的应用。从键盘读取学生的学号、姓名和三门课的成绩信息,计算每一个学生的平均成绩,然后输出所有信息。程序源代码如下:#include#defineN3/学生数目structstudentintno;charname20;floatscore3;floataverage;#defineStustructstudentvoidmain()StusN;inti,j;floattmp=0;printf(请输入%d个学生的信息:n,N);for(i=0;iN;i+)printf(学生学号:);scanf(%d,&si.no);printf(学生姓名:);scanf(%
21、s,si.name);printf(学生三门课成绩:);tmp=0;for(j=0;j3;j+)scanf(%f,&si.scorej);tmp=tmp+si.scorej;si.average=tmp/3;printf(学号t姓名t成绩t成绩t成绩t平均成绩n);for(i=0;iN;i+)printf(%dt%s,si.no,si.name);for(j=0;j成员名。例如:p-price=3.5;【案例6-4】结构体成员的输出。/三种输出方法比较#includestructproductcharcode6;charname20;floatprice;voidmain()structpro
22、ductapple=FI002,apple,3.5;structproduct*p;p=&apple;printf(%st%st%.2fn,apple.code,apple.name,apple.price);printf(%st%st%.2fn,(*p).code,(*p).name,(*p).price);printf(%st%st%.2fn,p-code,p-name,p-price);程序运行结果如下:FI002apple3.50FI002apple3.50FI002apple3.50从上面的案例中可以看出,这三种用于引用结构体成员的方法是完全等效的。对于普通数组,数组名是数组的首地址
23、,可以根据指向数组的指针或指向数组元素的指针对数组元素进行访问。同样,结构体数组或数组元素也可以用指针来访问。指针变量可以指向一个结构体数组,这时指针变量的值为整个结构体数组的首地址;结构体指针也可以指向结构体数组中的一个元素,这时指针变量的值为该结构体数组元素的首地址。如果一个指针变量已经指向一个结构体数组,就不能再使之指向结构体数组中元素的某一成员。例如:structpointdoublex;doubley;structpoints10,*p;p=s;即定义了一个指针变量p,让其指向一个结构体数组的首地址。此时p指向该结构体数组的第1个元素s0,p+1指向第2个元素s1,p+i则指向数组中
24、si。对结构体数组中元素成员的引用方式如下:(p+i)-x=3.5;(p+i)-y=4;【案例6-5】价格排序。/对多个商品价格进行排序#include#defineN5structproductcharcode6;charname20;floatprice;voidmain()structproductproN;structproduct*p,tmp;p=pro;inti,j,max;printf(请输入%d个商品的信息:n,N);for(i=0;icode);printf(商品名称:);scanf(%s,&(p+i)-name);printf(商品价格:);scanf(%f,&(p+i)-
25、price);for(i=N-1;i=0;i-)max=i;for(j=0;jprice)price)max=j;if(max!=i)tmp=promax;promax=proi;proi=tmp;printf(商品按价格排序结果为:n,N);printf(编号t名称t价格n);for(i=0;icode,(p+i)-name,(p+i)-price);程序运行结果如下:请输入4个商品的信息:第1个商品的信息:商品编号:FR001商品名称:apple商品价格:5.4第2个商品的信息:商品编号:FR002商品名称:orange商品价格:3.4第3个商品的信息:商品编号:FR003商品名称:ban
26、ana商品价格:4.6第4个商品的信息:商品编号:FR004商品名称:grape商品价格:8商品按价格排序结果为:编号名称价格FR002orange3.40FR003banana4.60FR001apple5.40FR004grape8.006.1.3结构体和函数结构体和函数的关系可以从两方面来看,一是函数的参数为结构体类型;二是函数返回值的类型为结构体,即函数的类型为结构体类型。1结构体作为函数参数结构体数据作为函数的参数可以分为3种情况:(1)结构变量的成员作为函数实参:把一个结构体变量的成员像普通变量一样按值传递,此时在被调用函数中此值不再是一个结构,而是一个基本数据类型的普通变量。(2
27、)结构体变量作为函数实参:将一个结构体变量作为函数的参数进行整体的传递,但是要将全部成员值逐个地传递,特别是当成员是数组时,将会使传递的时间和空间开销很大,程序运行效率低。(3)结构体指针作为函数实参:用指针变量或者结构体数组名作为函数的参数进行传递,这时实参传递给形参的是地址,从而减小空间和时间的开销,提高效率。【案例6-6】结构体指针作为函数参数。#includestructstudentintno;charname20;voidf1(intx)printf(%d,x);voidf2(structstudents)structstudenta=2012004,GaoFei;s=a;void
28、f3(structstudent*s)structstudenta=2012005,ZhangHong;*s=a;voidf4(structstudent*s)structstudenta2=2012006,WangWei,2012007,LiLi,*p=a;+s;+p;*s=*p;voidmain()structstudents1=2012001,ZhaoMing;structstudents22=2012002,LiQiang,2012003,XuLong;printf(f1运行结果:);f1(s1.no);/结构体变量的成员作为函数参数printf(nf2运行结果:);f2(s1);/结
29、构体变量作为函数参数,传值调用printf(%dn,s1.no);printf(f3运行结果:);f3(&s1);/结构体变量作为函数参数,传址调用printf(%dn,s1.no);printf(f4运行结果:);f4(s2);/指针变量作为函数参数printf(%d%dn,s20.no,s21.no);程序运行结果如下:f1运行结果:2012001f2运行结果:2012001f3运行结果:2012005f4运行结果:201200220120072结构体类型的函数当函数的返回值为结构体类型时,称该函数为结构体类型函数。结构体类型函数的一般定义形式为:struct结构体类型名函数名(形式参数列
30、表)函数体【案例6-7】结构体类型函数应用。从键盘读取学生的学号、姓名和三门课的成绩信息,根据读取的信息计算学生平均成绩并编写函数求平均分最高的学生信息。#include#defineN3/学生数目structstudentintno;charname20;floatscore3;floataverage;#defineStustructstudentstructstudentmax_score(structstudent*p)inti,max=0;for(i=0;iaverage(p+max)-average)max=i;return*(p+max);voidmain()structstud
31、entsN,max;inti,j;floattmp=0;printf(请输入%d个学生的信息:n,N);for(i=0;iN;i+)printf(学生学号:);scanf(%d,&si.no);printf(学生姓名:);scanf(%s,si.name);printf(学生三门课成绩:);tmp=0;for(j=0;j3;j+)scanf(%f,&si.scorej);tmp=tmp+si.scorej;si.average=tmp/3;max=max_score(s);printf(平均成绩最高的学生信息为:n);printf(学号t姓名t成绩t成绩t成绩t平均成绩n);printf(%d
32、t%s,max.no,max.name);for(j=0;j3;j+)printf(t%.2f,max.scorej);printf(t%.2fn,max.average);程序运行结果如下:请输入3个学生的信息:学生学号:20120601学生姓名:Jack学生三门课成绩:789689学生学号:20120602学生姓名:Lucy学生三门课成绩:766980学生学号:20120603学生姓名:Lily学生三门课成绩:939691平均成绩最高的学生信息为:学号姓名成绩成绩成绩平均成绩20120603Lily93.0096.0091.0093.336.1.4任务实现1问题描述对于一组学生信息按其平均
33、成绩进行排序,并输出排序结果。学生信息包括学号、姓名以及三门课的成绩,并打印排序后的学生信息。2要点解析学生成绩的排序算法之前已经学过,但是要显示按成绩排序后的学生的完整信息,则需要用结构体来存储数据。学生信息结构体可以定义为:structstudentintno;charname20;floatscore3;floataverage;然后编写算法,对结构体中的average信息进行排序,再输出排序后的学生信息。3程序实现#includestructstudentintno;charname20;floatscore3;floataverage;#defineStustructstudentS
34、tu*sort(Stu*p,intn);voidmain()structstudents100,*p;inti,j,no,num=0;floattmp=0;printf(请输入学生的信息,以学号为结束:n);printf(学生学号:);scanf(%d,&no);while(no!=0)snum.no=no;printf(学生姓名:);scanf(%s,snum.name);printf(学生C语言、Java和数据库的成绩:);tmp=0;for(i=0;i3;i+)scanf(%f,&snum.scorei);tmp=tmp+snum.scorei;snum.average=tmp/3;nu
35、m+;printf(学生学号:);scanf(%d,&no);printf(按平均成绩排序后的学生信息为:n);printf(学号t姓名tC语言tJavat数据库t平均成绩n);p=s;p=sort(p,num);for(i=0;inum;i+)printf(%dt%s,pi.no,pi.name);for(j=0;j=0;i-)max=i;for(j=0;javerage)average)max=j;if(max!=i)tmp=pmax;pmax=pi;pi=tmp;returnp;程序运行结果如下:请输入学生的信息,以学号为0结束:学生学号:20120601学生姓名:Gaocheng学生C
36、语言、Java和数据库的成绩:788284学生学号:20120602学生姓名:Lipeng学生C语言、Java和数据库的成绩:687578学生学号:20120603学生姓名:Wangming学生C语言、Java和数据库的成绩:908794学生学号:20120604学生姓名:Zhanggong学生C语言、Java和数据库的成绩:697672学生学号:0按平均成绩排序后的学生信息为:学号姓名C语言Java数据库平均成绩20120604Zhanggong 69.0076.0072.0072.3320120602Lipeng68.0075.0078.0073.6720120601Gaocheng78.
37、0082.0084.0081.3320120603Wangming90.0087.0094.0090.33任务6.2学生成绩排序链表任务目标了解动态内存管理的概念。掌握动态内存管理函数的使用方法。了解链表的概念以及链表的优点。掌握链表的创建和输出方法。掌握集中链表的基本操作方法,包括链表的查找、插入和删除。用链表实现学生成绩排序任务。6.2.1动态内存管理已知,变量的形式均为静态数据类型,即变量所占内存空间的大小在程序的说明部分已经确定,在程序的运行过程中不能加以改变。例如学生成绩管理系统,有时无法确定学生数目,为了满足大多数情况的需要,往往把数组定义的大一些。例如:charname10020
38、;规定学生数目最大为100,每一个学生的姓名最长为19个字符(包括一个终止字符)。但这样会出现这样两个问题:(1)定义得再大也不能保证一定可以满足用户的需求。(2)如果定义得很大而实际用得很少就会浪费存储空间。如果学生数目实际超过100个,则多余的学生信息无法存储,而如果学生数目只有十几个或者二三十个,则会浪费大量的存储空间。但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用数组的办法很难解决。为了解决上述问题,C语言中提供动态数据结构,即在程序运行过程中动态地为变量分配内存空间,变量存储空间的大小可以在程序执行期间动态地变化。动态数
39、据结构中最基本的形式为链表和二叉树,它们在程序设计中非常有用。语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,可以有效地利用内存资源。下面介绍几种常用的内存管理函数,这几个函数所在的头文件均为stdlib.h,使用前需加以说明。1malloc函数malloc函数的基本形式为:void*malloc(unsignedsize);其功能为在内存的动态存储区的自由内存部分中分配一个长度为size字节的连续内存区域。函数的返回类型是void*类型。void*表示未确定类型的指针。C、C+规定,void*类型可以强制转换为任何其他类型的指针。如果执
40、行成功,则其返回值为指向该内存区域首字节的指针;若执行不成功,则其返回值为0,即NULL。“size”是一个无符号数。该函数的调用形式为:(类型说明符*)malloc(size);“类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。例如:int*p=(int*)malloc(sizeof(int)*128);表示分配128个连续的整型存储单元,并将其首地址存储到指针变量p中。sizeof(int)表示一个整型数据的长度,乘以128表示128个整型数据的空间。如果写成:int*p=(int*)malloc(1);代码也能通过编译,但事实上只分配了1个字节
41、大小的内存空间,当往里存入一个整数时,就会有3个字节无家可归,而直接“住进邻居家”,造成的结果是后面的内存中原有数据内容被改写。structstudent*p=(structstudent*)malloc(sizeof(structstudent);表示分配一个长度为structstudent长度的一段内存空间,其首地址赋值给p。在指针使用前最好先测试返回值是否为空指针,因为使用空指针会破坏系统。2calloc函数calloc函数的基本形式为:void*calloc(unsignedn,unsignedsize);其功能为,在内存动态存储区的自由存储部分中,分配n个长度为size字节的连续存储
42、空间。如果分配成功,则返回值为连续存储空间的首地址;若执行不成功,则返回值为0,或者返回值为NULL。calloc函数的调用形式:(类型说明符*)calloc(n,size);calloc函数与malloc函数的区别仅在于一次可以分配n块区域。例如:ps=(struetstudent*)calloc(2,sizeof(structstudent);其中的sizeof(structstudent)是求student的结构长度。因此该语句的意思是:按student的长度分配两块连续区域,强制转换为student类型,并把其首地址赋予指针变量ps。3free函数free函数的基本形式为:voidfr
43、ee(void*ptr);其功能为释放由ptr指向的内存,释放的内存回复为自由内存,可以再被分配。ptr是指针类型或者指针变量,它的值通常为malloc函数或者calloc函数调用之后的返回值。如果ptr为空,free函数则什么都不做。free函数的调用形式:free(void*ptr);例如:free(p);表示释放指针变量p所指向的一段内存单元。如果ptr的值不是由malloc或者calloc函数调用时的返回值,则调用时很可能会毁坏内存管理机制,并且破坏系统。在编写程序的过程中,对于动态申请的内存空间,使用完之后一定要及时回收,否则这块内存空间不能再继续使用,即这块内存泄露了。内存泄漏是指
44、由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。很多人觉得计算机病毒很神秘,其实计算机病毒无非是一个恶意的程序。以下就是一个简单的恶意程序,它具有一定的破坏性。如果把这个程序完善一下,使其具有隐蔽性、传染性和自发运行等功能,则为一个标准的病毒程序。#include#includeintmain(void)void*s;while(1)/为死循环s=malloc(50);/申请内存空间/Malloc函数迟早会由于内存泄漏而返回NULL程序中申请了内存
45、却没有释放,导致内存泄漏。当程序不停地重复调用这个有问题的函数f,申请内存函数malloc()最后会在程序没有更多可用存储器可以申请时产生错误(函数输出为NULL)。但是,由于函数malloc()输出的结果没有加以出错处理,因此程序会不停地尝试申请存储器,并且在系统有新的空闲内存时,被该程序占用。该程序一旦运行,将很快耗尽计算机中的内存,造成死机。【案例6-8】动态存储分配。#include#includevoidmain()structstuintnum;char*name;charsex;floatscore;*ps;ps=(structstu*)malloc(sizeof(structs
46、tu);ps-num=102;ps-name=Zhangping;ps-sex=M;ps-score=62.5;printf(Number=%dnName=%sn,ps-num,ps-name);printf(Sex=%cnScore=%.2fn,ps-sex,ps-score);free(ps);程序运行结果如下:Number=102Name=ZhangpingSex=MScore=62.506.2.2链表的创建1链表的概念链表是一种常见的基础数据结构,也是实现非连续存储的一种结构。非连续存储和连续存储的区别在于非连续存储能够更好地利用剩余的存储空间。将逻辑上相邻的数据分配在一块连续的内存空
47、间内,逻辑关系通过数据存储单元的连接关系来实现,这样的存储方式称为顺序存储方式,数组即时遵循这样的存储规则。显然,顺序存储方式需要一块足够大的连续的内存空间,并且其长度是事先定义好的。而在内存的不断动态分配和回收的过程中,很可能将自由内存分为大小不一的内存块,导致无法获取一大块连续的内存空间。这是需要将逻辑上相邻的数据分配到物理上离散的存储单元中,然后通过一定的规则可以访问每一段存储单元,使这些存储单元逻辑上相邻,这种存储方式为链接存储。由此可见,所谓链表即把存放在不同地点的数据用地址链条串接而成的数据链。组成链表的每一个元素是一个数据块,又称为链表的结点。结点由两部分组成:(1)数据域用来存
48、储本身数据。(2)指针域用来存储下一个结点地址或者说指向其直接后继的指针。指针域的作用为把数据按照逻辑关系组织在一起,其作用好像一个链,将所有数据元素一个接一个地连起来。第一个结点的指针域中存放第二个结点的首地址,然后在第二个结点的指针域内存放第三个结点的首地址,如此串联下去直到最后一个结点。链表的存储结构如图6-1所示。图6-1链表存储结构图每一个结点都指向逻辑关系中的下一个结点的地址,因此称为单链表。head为头指针,不存储数据,指向单链表的第一个结点,是链表操作的唯一入口。第一个结点中存储的数据为12,地址为2000,按照这个地址找到第二个结点,第二个结点中数据域中值为整型数据34,指针
49、域中值为1800,即下一个结点的地址值为2000。链表中最后一个结点没有后继结点,因此其指针域为NULL(或者用0表示)。按此结构存储的链表,每一个结点的地址都存放在其前面一个结点中,所以无论要访问链表中哪一个结点,都必须从头开始找起,否则无法获取该结点的地址。可以用动态内存分配的方法为一个链表分配内存空间,每次分配一块内存空间用来存放链表中的一个结点,有多少个数据要存储就分配多少块内存空间,也就是建立多少个结点。无须事先确定结点的数目,且可以随时删掉任意一个结点,并释放该结点所占用的内存空间,从而节约内存资源。C语言中一个结点的结构设计如下:struct结构体名数据成员列表;struct结构
50、体名*指针名;例如:structnodeintdata;structnode*next;/指向结点的指针;同一个链表中每一个结点的数据类型都是相同的,因此,在结点中存放下一个结点地址的指针变量类型也是相同的。可以用该类型声明定义结点:#definestructnodeNODENODEp;p.data=10;p.next=NULL;2链表的创建链表的建立是指,在程序执行过程中,建立起一个一个的结点,并将它们按照逻辑顺序链接成一串,形成一个链表的过程。xintdata;structnode*next;/指向结点的指针;同一个链表中每一个结点的数据类型都是相同的,因此,在结点中存放下一个结点地址的指