第7章2_ppt.txt

大学实用C语言程序设计教程-陈建铎-大学教学资料课件PPT

收藏

资源目录
跳过导航链接。
大学实用C语言程序设计教程-陈建铎-大学教学资料课件PPT.zip
压缩包内文档预览:(预览前20页/共21页)
预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图 预览图
编号:21835837    类型:共享资源    大小:13.46MB    格式:ZIP    上传时间:2019-09-06 上传人:QQ24****1780 IP属地:浙江
25
积分
关 键 词:
大学 实用 语言程序设计 教程 陈建 教学 资料 课件 ppt
资源描述:
大学实用C语言程序设计教程-陈建铎-大学教学资料课件PPT,大学,实用,语言程序设计,教程,陈建,教学,资料,课件,ppt
内容简介:
本章学习C语言的三种数据类型,分别是构造类型中的结构体和共用体类型,以及基本类型中枚举类型。 本章先给出结构体类型的定义,着重讲述结构体变量的定义、引用、初始化,结构体数组,结构体变量的指针以及结构体的应用;然后讲述共用体和枚举类型;同时介绍动态分配内存的函数和typedef语句。第7章 结构体与共用体第7章 结构体与共用体7.1 结构体类型和结构体类型变量 7.2 结构体数组 7.3 指向结构体类型数据的指针 7.4 内存的动态分配与单链表 7.5 共用体7.6 位段7.7 枚举类型7.8 typedef语句7.1 结构体类型和结 构体类型变量 7.1.1 结构体类型及其定义7.1.2 结构体类型变量的定义7.1.3 结构体类型变量及其成员的引用7.1.4 结构体变量的初始化7.1.1 结构体类型及其定义 数组属于构造类型,数组中的各元素必须属于同一个数据类型。但是在实际生活中,我们经常会遇到有若干各不同类型的数据组合成一个有机的整体。这些组合在一个整体中的数据是互相联系的。例如,要描述一个学生的相关信息,需要有学号、姓名、性别、出生日期、成绩(英语、语文、数学)、联系电话等项内容,这些项都与某一学生相联系。对每一项来说,它可以是某一基本类型,也可以是一定义的构造类型。如我们可以规定学号(num)为字符型数组、姓名(name)为字符型数组、性别(sex)为字符型、出生日期(birthday) 为字符型数组、成绩(score3) 为实型数组、联系电话(tel) 为字符型数组。图7-1显示了一学生的信息。C语言允许用户自己指定这样一种包含若干个类型不同(当然也可以相同)的数据项的数据结构,它称为结构体(structure)。组成结构体的数据项成为结构体成员。在其他高级语言中称其为“记录”。 图 7-1结构体是一种构造数据类型,其用途是把不同类型的数据组合成一个整体,当作一个新的数据类型来使用。在C语言中,用如下方式定义 struct student char num10; char name20; char sex; char birthday8; float score3; char tel12; ;声明结构体类型时所必须使用的关键字结构体类型的名称结构体成员列表,包括每个结构体成员的类型及名称分号不可少在struct student类型起作用的范围内,struct student和系统提供的标准类型(如int、char、float、double等)一样具有同样的地位和作用,都可以用来定义变量的类型。定义一个结构体类型的一般形式为:struct 结构体名 成员表列 ;说明:(1)“结构体名”用作结构体类型的标志,它又称“结构体标记”(structure tag)。类型与变量是不同的概念,不要混同。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。类型名的取名规则遵从标识符。(2)成员列表为本结构体类型所包含的若干个成员的列表,必需用 括起来,并以分号结束。每个成员的形式为 类型标识符 成员名; 成员的类型可为除该结构体类型外的任何一种类型,如基本类型、指针类型和结构体类型。若定义结构体类型struct date如下:struct date int year; int month; int day; ;则结构体类型struct student中的成员birthday就可定义为 struct date birthday;这样birthday就不是字符型数组,而是结构体类型struct date的变量。(3)成员名命名规则与变量名相同。同一结构体类型中的各成员不可互相重名,但不同结构体类型间的成员可以重名,并且成员名还可与程序中的变量重名,因为它们代表着不同的对象。 有了结构体类型,它仅相当于一个模型,但其中并无具体数据,系统对它不分配实际内存单元,为了能在程序中使用结构休类型的数据,就须在此类型基础上定义变量。结构体类型变量因其所基于的类型是自定义的,故有三种形式的定义方法,分别是: 7.1.2 结构体类型变量的定义1. 先声明结构体类型再定义结构体变量,一般形式是: struct 结构体名 变量名表;如 struct student stud1, stud2; struct date date_1;定义了3个结构体类型的变量stud1,stud2和date_1,前两个是struct student类型的结构体变量,后者是struct date类型结构体变量。 2. 在声明结构体类型的同时定义变量,一般形式是: struct 结构体名 成员表列 变量名表;如: struct date int year; int month; int day; date_2,dd;同样可定义date_2,dd为struct date类型的变量。3. 直接定义结构体类型变量,其一般形式为: struct 成员表列; 变量名表;即不出现结构体名。如struct char num10; char name20; char sex; struct date birthday; float score3; char tel12; stud3,stud4;定义了2个结构体类型的变量stud3,stud4。 定义了结构体型变量之后,结构体类型名的任务就完成了,在后续的程序中除求类型的长度和强制类型转换外,不再对其操作,而只对这些变量(如studl,stud2,date_2,dd等)进行赋值、存取或运算等操作。在编译时,对类型不分配空间,只对变量分配空间。结构体型变量所占内存空间是各成员变量所占内存单元的总和,各成员间占用的存贮单元是连续的。 就好像指针变量一样,有些是指向整型数据的指针变量,有些是指向实型数据的指针变量,后面还会见指向结构体类型数据的指针变量。对于结构体类型变量也类似,有些是struct student的结构体变量,有些是struct date的结构体变量,有些是其它结构体类型的结构体变量。如果说指针变量要看它指向何种类型的指针,那么结构体变量要看它到底是那种具体的结构体类型的变量。结构体是构造类型,不像基本类型那样,在任何时候其含义是一致的,而用户可以构造多个结构体类型,甚至对同一结构体标记在不同的程序中,其含义可以不同(指成员不完全相同)。这一点一定要引起足够的重视。 定义结构体类型变量的目的就是为在后续程序中引用它。由于结构体类型的变量是一种构造类型的变量,可引用的对象有两个:变量名代表变量的整体,成员名代表变量的各个成员,二者均可在程序中引用。引用时注意以下规则: 1变量成员的引用方法(成员运算符“”):如前例在结构体类型struct student下定义的两个变量7.1.3 结构体类型变量及其成员的引用studl, stud2,二变量中的每个成员均可引用,且所引用的成员变量与其所属类型的普通变量一样可进行该类型所允许的任何运算。例如:aver=(stud1.score0+ stud1.score1+ stud1.score2)/3;strcpy(,Wang zhe);stud2.score1= stud1.score1;n=dd.year-date_1.year;date_1.year+;day=stud3.birthday.day-;scanf(%s %c %f,, &studl.sex,&stud1.score1);printf(%d %d %dn,dd.year,dd,month,dd.day); 在C语言的运算符中,取成员运算符“”优先级最高,故以上语句均为对引用之后的成员变量进行操作。若结构体定义是嵌套的,则只能引用最低级的成员(用若干“”运算符,逐级引用到最低级)。如 stud3.birthday.year是合法的,而 stud3.year是非法的。 2. 结构体类型变量可以整体引用来赋值。如stud2studl;即将变量studl的所有成员的值一一赋给变量stud2的各成员。结构体型变量只能对逐个成员进行输入或输出,不可进行整体的输入输出,如: printf(%s%s%c%f%f%f%s,stud2); 是错误的。 3. 结构体类型变量占用的一段存贮单元的首地址称为该结构体类型变量的地址,其每个成员占用的若干单元的首地址称为该成员的地址,两个地址均可引用。如: printf(%o,&stud1); 同其它类型变量一样,对结构体变量也可以在定义时指定初始值,称为为初始化。所有结构体变量,不管是全局变量还是局部变量,自动变量还是静态变量均可如此。 7.1.4 结构体变量的初始化例7-1 结构体变量的初始化及输入输出的例子。#include stdio.h#include string.hstruct dateint year; int month; int day; dd=1997,7,1;struct studentchar num10; char name20; char sex; struct date birthday; float score3; char tel12; ;main() struct student stud=04010538,F, 1983,5,28,86.5,45,97,13389266899; strcpy(, Liu lihua); scanf(%f %f,&stud.score1,&stud.score2); scanf(%s,stud.tel); dd.year+; printf(%s %s %c %d %d %d %4.1f %4.1f %4.1f %sn,stud.num, stud.sex, stud.birthday.year,stud.birthday.month,stud.birthday.day, stud.score0,stud.score1,stud.score2,stud.tel);printf(%d %d %dn,dd.year,dd.month,dd.day); 程序执行结果如下: 78 90.5 13345678900 (键盘输入) 04010538 Liu li hua s 1983 5 28 86.5 78.0 90.5 13345678900 1978 7 1(1) 两个头文件包含是必需的,因为程序中要用到其中定义的标准函数;(2) 程序中定义了两个结构体类型,由于它们的定义在所有函数之外,称其为外部结构体类型,在其定义语句之后直到本文件结束之前,该类型均有效,既可用来定义结构体类型的变量。如果结构体类型是在某函数内定义的,称其为内部结构体类型,其有效范围当然只能是本函数了。此外,由于结构体类型struct student的成员birthday的类型是结构体类型struct date,所以结构体类型struct date的定义必须放在结构体类型struct student的定义之前;(3) 字符数组的初值是字符串常量,在赋初值时加不加” ”可以。如04010538也可写成04010538;(4) 在用scanf语句输入结构体变量的成员时,输入表列同样要用地址。如&stud.score1,&stud.score2和stud.tel都是地址(stud.tel是数组名)。对字符串的赋值要用strcpy函数。7.2 结构体数组 7.2.1 结构体数组的定义7.2.2 结构体数组的初始化7.2.3 结构体数组的应用7.2.1 结构体数组的定义 结构体数组就是所有元素都是同一类型的结构体数据的数组。结构体数组中的各元素在内存中是连续存放的。定义结构体数组的方法和定义结构体变量的方法相同,也有三种方式。如 struct student stud510; struct chengji int num; char name10; float s5; float aver; stu20;定义了两个结构体数组stud5和stu。 结构体数组也可在定义时赋值,即初始化。方法与结构体和数组的初始化相同。如 struct chengji stu = 121,wang feng,50,60,76,85,90,0, 122,li hui,67,87,71,85,58,1,123,gao lu,77,81,67,75,62,2; 见图7.2.2 结构体数组的初始化 例7-2 学生信息struct chengji的结构体类型,为求4个学生的5门功课成绩的平均分并输出。程序如下: 7.2.3 结构体数组的应用main() int i,j; float sum; struct chengji int num; char name10; float s5; float aver; stu =111,zhang san,67,89,76,54,86,105,li si,87,73,88,75,78,111,wang wu,79,82,56,64,77, 107,qian liu,60,53,91,75,65,0 ; for (i=0;i4;i+) sum=0.0; for (j=0;j5;j+) sum=sum+stui.sj; stui.aver=sum/5.0; printf(%d %-10s %3.0f %3.0f %3.0f %3.0f %3.0f%5.1fn,stui.num,,stui.s0,stui.s1,stui.s2,stui.s3,stui.s4,stui.aver); 程序执行结果是:111 zhang san 67 89 76 54 86 74.4105 li si 87 73 88 75 78 80.2111 wang wu 79 82 56 64 77 71.6107 qian liu 60 53 91 75 65 68.8 结构体变量可以作为函数的参数,此时是值传递。例7-3 用函数实现例7-2的求平均值的运算。程序如下: struct chengji int num; char name10; float s5; float aver; ; float f(struct cj stud) int j; float sum=0; for (j=0;j5;j+) sum=sum+stud.sj; return sum/5.0; main()int i,j;struct chengji stu =111,zhang san,67,89,76,54,86,105,li si,87,73,88,75,78,111,wang wu,79,82,56,64,77,107,qian liu,60,53,91,75,65,0 ;for (i=0;i4;i+) stui.aver=f(stui); printf(%d %-10s %3.0f %3.0f %3.0f %3.0f %3.0f %5.1fn,stui.num,,stui.s0,stui.s1,stui.s2,stui.s3,stui.s4,stui.aver); 此程序与例7-2程序的执行结果一致。但结构体struct chengji应为外部结构体类型。例7-4 有10个选举人,3个候选人,每人只能投一个候选人的票,统计3个候选人的总票数。程序如下:#include stdio.h#include string.hstruct person char name8; int count; leader3=ZHANG,0,WANG,0,LI,0; main() int i,j; char lead_name8;for (i=1;i=10;i+) scanf(%s,lead_name); for (j=0;j3;j+) if (strcmp(lead_name,)=0) leaderj.count+; for (j=0;j结构体变量的成员这里,“-”是指向运算符,运算优先级与“( )”、“ ”和“”的运算优先级相同,最高优先级别。 阅读下面的例子,熟悉用指向结构体变量的指针来引用结构体变量的成员的方法。例7-5 用指针引用结构体成员的例子。#include string.hstruct abc char name10; char sex; int salary; ;main() struct abc ww,*p=&ww; strcpy(,wang hao); ww-sex=M; ww.salary=1234; printf(%s %c%dn,ww.sex,ww.alary); printf(%s %c %dn,p-name,p-sex,p-salary); printf(%s %c %dn,(*p).name,(*p).sex,(*p).salary); 程序执行结果是:wang hao M 1234wang hao M 1234wang hao M 1234可见,三种方式对结构体成员引用的效果是相同的。 使用指向结构体数组的指针来访问数组,既方便了数组元素的引用,又提高了数组的利用率。例7-6 用结构体数组的指针完成例7-2。程序如下:7.3.2 指向结构体数组的指针main()int i,j; float sum; struct chengji int num; char name10; float s5; float aver; *p,stu = 111,zhang san,67,89,76,54,86, 105,li si,87,73,88,75,78,111,wang wu,79,82,56,64,77,107,qian liu,60,53,91,75,65,0 ; p=stu; for (i=0;i4;i+,p+) sum=0.0; for (j=0;jsj; stui.aver=sum/5.0; printf(%d %-10s %3.0f %3.0f %3.0f %3.0f %3.0f %5.1fn, p-num,p-name,p-s0,p-s1,p-s2, p-s3,p-s4,p-aver); 程序执行结果不变。注意,p的初值是数组stu的首地址,通过p+指向不同的数组元素。假如p指向第2个数组元素(li si),请读者分析p-s0、 p-s0+、(p-s0)+、(+p)- s0和(p+)-s0分别会得到什么结果。 将一个结构体变量的值传递给另一个函数,有3个方法: 用结构体变量的成员作实参 用法和用普通变量作实参是一样的,属于“值传递”。2. 用结构体变量作参数 用法和用普通变量作参数是一样的,也属于“值传递”。3. 用指向结构体变量(或数组)的指针作参数 属于“地址传递”,程序效率高。7.3.3 指向结构体数组的指针 例7-7 用指向结构体变量(或数组)的指针作参数,求n个学生的平均成绩最高的学生的信息并输出。struct CJ int num; char name10; float s5; float aver; ;struct CJ *fun(struct CJ *pstud,int n) struct CJ *p,*p_max,*p_end; int j; float max=0; p=pstud; p_max=p; p_end=p+n; for ( ;pp_end;p+) float sum=0; for (j=0;jsj; sum=sum/5.0; p-aver=sum; if (summax) max=sum; p_max=p; return p_max; 返回结构体struct CJ类型指针的函数funmain()int i,j; struct CJ *pp,stu = 111,zhangsan,67,89,76,54,86,105,li si,87,73,88,75,78,111,wang wu,79,82,56,64,77,107,qian liu,60,53,91,75,65,0 ; pp=fun(stu,4); printf(%d %-10s %3.0f %3.0f %3.0f %3.0f %3.0f %5.1fn,pp-num, pp-name,pp-s0,pp-s1,pp-s2,pp-s3,pp-s4,pp-aver);程序执行结果如下:105 li si 87 73 88 75 78 80.27-8 修改例7-7的程序,用指针的指针参数,传递平均成绩最高的学生的指针。修改后的程序如下:void fun(struct CJ *pstud, struct CJ *p_max, int n) struct CJ *p,*p_end; *p_max=p; main() int i,j;struct CJ *pp,stu=;fun(stu,&pp,4); printf( pp-num);欲传递回指针值,应使用指针的指针图 7-27.4 内存的动态分配与单链表 本节先给出数据在计算内存中进行动态分配的概念和内存分配函数,然后介绍单链表及其有关操作的函数,它是动态分配内存、结构体、指针的综合应用。7.4 内存的动态 分配与单链表7.4.2 内存分配函数7.4.1 数据的存储结构7.4.3 链表的概念 凡能被计算机存储、加工的对象统称为数据。它可以是数值、字符串、表格、图像甚至语音等等。可以说数据就是计算机化的信息。 数据元素是数据的基本单位,在程序中作为一个整体而加以考虑和处理。数据元素又可称为元素、结点、顶点或记录。大多数情况下,数据元素是有若干个数据项组成。又称为字段或域。它是数据不可分割的最小标识单位。例如,有一张学生情况统计表,整个表就是一个数据,表中的每一行(描述某个学生的基本情况)就是一个数据元素,表中的每7.4.1 数据的存储结构一列(描述学生的学号、姓名等信息)就是一个数据项。 用计算机进行数据处理,需要考以下三个方面的问题,即数据的逻辑结构、数据的物理结构和数据的运算。 数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的。数据的逻辑结构可分为集合结构、线性结构、树形结构和网状结构。数据的存储物理(存储)结构是逻辑结构的存储实现(也称为映像),它是依赖于计算机语言的。对机器语言而言,存储结构是具体的,但我们只在高级语言的层次上来讨论存储结构。数据的物理结构分为顺序存储结构、链式存储结构、索引存储结构和散列存储结构。数据的运算是定义在数据的逻辑结构上的,每种逻辑结构都有一个运算或操作的集合。最常用的运算有:检索、插入、删除、更新、排序、合并等。但这些运算实际上是在抽象的数据上所施加的一系列抽象的操作,也就是说,我们只知道这些操作是“做什么”,即能完成什么功能,而无需考虑“如何做”,只有决定了存储结构以后,我们才能考虑如何具体实现这些运算。运算实现是完成运算功能的算法。 我们下通过一个例子来说明数据的存储结构。假如某公司要将它的所有客户的信息存储在计算机中以便进行处理,如查询客户、新增客户、删除客户、修改客户信息、对客户进行排序等。那么这些客户信息在计算机内存中如何进行组织(存储)呢?一个自然的想法就是把它们组成结构体数组,以数组的形式存放计算机内存中,这就是顺序存储结构。其特点是数据元素在内存中是连续的,可以进行直接存取。但对插入、删除运算的效率较低。另一个办法就是将各个客户的信息单独存放,即数据元素在内存中不要求连续,而专门设置一个指针来描述数据元素间的逻辑关系,这就是链式存储结构。其特点是数据元素在内存中不一定是连续的,能方便、快捷的实现插入和删除运算,能动态的分配内存,但不能进行直接存取。 内存是计算机的最宝贵的资源。链式结构与顺序结构相比的另一个优点就是能有效的使用内存。顺序结构要求把数据所用的空间一次分配完成,由于数组的维大小是固定,为了留有余地,往往有内存空间的浪费;而链式结构对内存空间是动态分配的,即在需要时每次只分配一个数据元素所需的空间,便于对的内存碎块的有效利用,对不再使用的内存空间可以及时的释放。C语言提供了关于内存动态分配的函数。 C语言提供了专门用来进行动态存储分配的标准库函数。头文件名为stdlib.h或malloc.h。现分别介绍如下: 1. malloc函数函数原型:void *malloc(unsigned size)功能:分配size个字节的存储区。返回值:所分配的内存区地址,若内存不够,返回0。 2. Free函数函数原型:void free(p)功能:释放p所指的内存区。返回值:无。7.4.2 内存分配函数 3. calloc函数函数原型:void *calloc(unsigned n, unsigned size)功能:分配n 个数据项的内容的连续空间,每个数据项的大小为size。返回值:所分配的内存区地址,若内存不够,返回0。 4. realloc函数函数原型:void *realloc(void *p,unsigned size)功能:将p所指的已分配内存区的大小改为size。返回值:返回指向该内存区的指针。注:以上的void均可改为char。例7-9 动态分配内存的例子。#include stdlib.hmain()double *p,*q; printf(%o %on,&p,&q); p=(double *)malloc(sizeof(double); if (p=0) printf(malloc errorn); exit (0); *p=1000000; printf(p=%o *p=%4.1fn,p,*p); free(p); q=(double *)malloc(sizeof(double); if (q=0) printf(malloc errorn); exit (0); *q=2000000;printf(q=%o *q=%4.1f p=%o*p=%4.1fn,q,*q,p,*p); 程序执行结果如下:177716 177720p=4576 *p=1000000.0 q=4576 *q=2000000.0 p=4576 *p=2000000.0 链表是由零个或多个结点用链接指针串联在一起的数据结构。零个结点的链表称空链表。链表有单向链表、双向链表、循环链表等,我们在此只讨论单向链表,简称单链表。为方便叙述,以后提到链表均指单链表。 链表中的每个结点的数据有两部分组成,即数据元素的信息和链接指针信息。图7-3是字符串CHINA的链表示意图。7.4.3 链表的概念图 7-3 图中,head称为链表头指针,对链表的访问必须从头指针开始,逐个结点进行访问,指针值为NULL即0的结点是链表的最后一个结点。可以说,头指针是链表的唯一标识。为以后对链表尽心处理的方便,在链表的第1个结点之前附加一个结点,称其为头结点,链表的头指针指向头结点,头结点的指针指向链表的第1个结点。以后若不加说明,链表均带有头结点。 在C语言中,链表的存储又是如何具体实现呢?可先定义一个结构体类型,把每个结点作为结构体类型的变量,这个结构体类型的成员中除包括数据元素的信息外,一定还含应有一个表示结点链接的成员。把各个结点用指针连接起来,前面加上头结点,在设一个结构体类型的指针变量,让它指向头结点即可。 对上面的链表,其结构体类型及指针变量可定义如下: struct string char data; struct string *next; *head,*p1,*p2; 链表的操作主要有建立链表、输出链表、求链表的长度、查找结点、插入结点、删除结点、合并链表、逆置链表等。下面以结构体类型struct list int num; float data; struct string *next; 为数据结构,逐一给出实现上述操作的函数。7.4.4 链表的基本操作1. 建立链表 建立链表是使用链表进行一切操作的基础,只有建立了链表,才能对链表进行其它操作。建立链表的过程是从无到有的逐个为结点动态分配空间、输入结点内容、建立链接的过程。 建立链表的方法有头插法和尾插法两种。头插法是新建的结点总是插在已有链表的第1个结点之前,成为新的第1个结点;尾插法两种是新建的结点总是插在已有链表的最后一个结点之后,成为新的最后一个结点。 头插法建立链过程见图7-4。(a)空链表L (b)开辟新结点s (c)s插到L的首结点之前 (d)重复(b)和(c) ,直到所有结点插入完毕。 LSLSLSL 算法的思路是:设置两个指针变量L和s,L是头指针,s始终指向新开辟的结点;先开辟一个新结点,令其指针域为NULL,让L指向该结点,得到一个空链表;然后逐个输入结点的值,若学号值为结束标志-1,返回头指针L,否则,开辟新结点s,给s的数据成员赋值,把s插入 到第1个结点之前,即令 s-next=L-next;L-next=s; 注意,次序不可颠倒。例7-10 用头插法建立链表。struct list *CreateFromHead() struct list *L,*s; int no,flag=1; float x; L=(struct list *)malloc(sizeof(struct list); L-next=NULL; while(flag) scanf(%d%f,&no,&x); if(no!=-1) s=(struct list *)malloc(sizeof(struct list); s-num=no; s-data=x; s-next=L-next; L-next=s; else flag=0; return L; (a)空链表L (b)开辟新结点s (c)s插到L的 首结点之后 (d) 开辟新 结点s 图7-4 尾插法建立链表示意图 (e)s插到L的首结点之后 (f) 插入3个结点后的链表L 算法的思路是:设置3个指针变量L、s和r,L是头指针,s始终指向新开辟的结点, r是为指针,即r始终指向链表的最后一个结点结点;先开辟一个新结点,令其指针域为NULL,让L指向该结点,得到一个空链表,令r=L;然后逐个输入结点的值,若学号值为结束标志-1,令r-next=NULL,返回头指针L,否则,开辟新结点s,给s的数据成员赋值,把s插入到最后一个结点之后,即令 r-next=s; r=s;注意,次序不可颠倒。例7-11 用尾插法建立链表。struct list *CreateFromTail() struct list *L,*s,*r; int no,flag=1; float x; L=(struct list*)malloc(sizeof(struct list); r=L; while(flag) scanf(%d%f,&no,&x); if(no!=-1) s=(struct list*)malloc(sizeof(struct list); s-num=no; s-data=x; r-next=s; r=s; else flag=0; r-next=NULL; return L; 2输出链表 建立的链表是必须要打印输出的。输出链表就是从链表的第1个结点开始,输出每个结点的数据,直到最后一个结点的指针域为空为止。例7-12 输出链表中的各个结点。void PrintLinkList(struct list *Head) struct list *L,*p; p=Head-next; while(p) printf(%5d%5.1fn,p-num,p-data); p=p-next; printf(n); 3. 求链表的长度 链表的长度就是链表所包含的结点个数(不含头结点),方法与输出链表类似。例7-13 求链表的长度。int ListLength(struct list *L) struct list *p; int j; j=0; p=L-next; while(p!=NULL) p=p-next; j+; return j; 例7-14 建立链表、并输出链表结点及长度的主函数。#define NULL 0#include stdio.h#include string.hstruct list int num; float data; struct list *next; ;main() struct list *Head,; Head=CreateFromHead();/*Head=CreateFromTail();*/ PrintLinkList(Head); printf(“LIST Length=%d”, ListLength(Head);4. 查找指定的结点 查找指定的结点最终是获得结点的指针,故函数通常定义为返回指针值的函数。分为按结点的序号查找和按结点的值查找两种情况。 由于链表不能进行直接存取,要访问链表的第i个结点,必须从头指针开始,顺序找到第i个结点。同样,要访问链表的某成员值为key的结点,也从头指针开始,顺序找到其成员值为key的结点。例7-15 按结点的序号查找,返回结点的指针。struct list *GetNode(struct list *L,int i)struct list *p=L; int j=0; while(p-next!=NULL&jnext; j+; if(i=j)return p; else return NULL; 注意:循环条件p-next!=NULL不能写p!=NULL。例7-16 按结点的值(本例按data值)查找,返回结点的指针。 struct list *LocateKey(struct list *L,int key) struct list *p=L-next; while(p!=NULL) if(p-data!=key) p=p-next; else break; return p; 5. 删除结点 对于单链表来说,要删除一个结点(设指针为r),必须找到该结点的直接前驱结点的指针(设为p),令 p-next=r-next;free(r);则可可删除结点r并释放其内存空间。 p r图7-6 删除结点的示意图例7-17 删除第i个结点并带回其num、data值。int DeleteNode(struct list *L,int i,int *num1,int *data1) struct list *p=L,*r; int k=0; while(p-next!=NULL&knext; k+; if(k!=i-1) printf(Delete No. error!n); return 0; r=p-next; *num1=r-num *data1=r-data; p-next=r-next; free(r); return OK; 注意:被删除结点的num、data成员的值是通过指针变量带回的。6. 插入结点 要在第i个结点前插入一个值为num1、data1的结点,也必须先找到该结点的直接前驱结点的指针(设为pre),然后开辟新结点并赋值,最后令s-next=pre-next; pre-next=s使s插入到pre之后。图7-7 插入结点的示意图 例7-18 在第i个结点前插入一个值为num1、data1的结点。 int InsertNode(struct list *L,int i,int num1,int data1) struct list *pre=L,*s; int k=0; while(pre!=NULL&knext; k+; if(k!=i-1) printf(Insert NO. error!n); return 0; s=(struct list *)malloc(sizeof(struct list); s-num=num1; s-data=data1; s-next=pre-next; pre-next=s; return OK; 7. 合并链表例7-19 合并两个data值有序的链表La、Lb。struct list *MergeLinkList(struct list *La, struct list *Lb) struct list *Lc,*pa,*pb,*p; pa=La-next; pb=Lb-next; Lc=(struct list *)malloc(sizeof(struct list); p=Lc; while(pa&pb) if(pa-datadata) p-next=pa; pa=pa-next; p=p-next; else p-next=pb; pb=pb-next; p=p-next; if(pa)p-next=pa; if(pb)p-next=pb; return Lc; 8. 逆置链表 逆置链表就是使链表的第1个结点成为最后一个结点,第2个结点成为倒数第2个结点,最后一个结点成为第1个结点。 算法的思路是:设3个指向结点的指针p,q,h,h始终指向逆置后的链表的第1个结点(初值为NULL),p始终指向正准备处理的结点(初值为L-next),q始终指向p直接后继结点。当p为真时,作以下4步工作,记录p直接后继q,将p插在h之前,更新h,p后移到下一个结点,即q。最后令L-next=h即可。(a)有3个结点的原始链表(b) 对第1个结点操作后的情形(c)对第2个结点操作后的情形(d)对尾结点操作后的情形 图7-8 逆置链表的示意图例7-20 逆置链表。void InvertLinkList(struct list *L) struct list *p,*q,*h; h=NULL; p=L-next;, while(p) q=p-next; p-next=h; h=p; p=q; L-next=h; 请读者自己编写主函数,调用例7-15到例7-20的功能函数,上机调试执行程序。7.5 共用体7.5.1 共用体的概念7.5.2 共用体类型及共用体类型变量的定义 7.5.3 共用体变量的引用7.5.4 使用共用体应注意的问题7.5.1 共用体的概念 C语言允许用户把若干个不同类型的数据组合在一体,作为结构体类型,也可作为共用体类型,二者都属于构造类型。二者的不同之处在于,结构体类型中各个数据均作为结构体类型的一个成员,它们分别占用一定个存储单元,而共用体类型中各个数据在内存占用的字节数不尽相同(由于数据类型不一定完全相同),但都从同一起始地址的存储单元,即使用覆盖技术,使几个变量相互覆盖。这种使几个不同的变量共同占用同一段内存的结构,称为共用体类型的结构。公用体类型的定义形式为: union 公用体名 成员表列; ;公用体的成员表列的定义与结构体相同。有了公用体类型就可以定义公用体类型变量,同结构体一样,公用体类型变量也有三中定义方式。如7.5.2 共用体类型及 共用体类型变量的定义union datachar c;int i;float f; a,b;union char c; int i; float f; a,b;以及先定义公用体类型union data,在定义变量a,b为union data类型,即 union data a,b;都是定义了公用体类型变量a,b。图7-9 公用体类型变量a在内存中的存储情况公用体类型变量a,b在内存中各占4个字节(见图7-9),正好是占用字节数最多的成员所需的字节数。如果是以上述3个成员定义的结构体变量,则每个结构体变量在内存中各占7个字节。7.5.3 共用体变量的引用 共用体类型变量的引用方式与结构体类型变量的引用方式相同。如共用体型变量a的成员引用可以是 scanf(“%d”,&a.i); printf(“%f”,a.f); 但其整体引用,如: printf(“%d”,a); a=5; k=a; 等都是错误的。 使用共用体型变量时要注意以下几点:(1) 一个共用体型变量可以用来存放几种不同类型的成员,自然无法同时实现。即每一个瞬间只有一个成员起作用。因各成员共用一段内存,彼此互相覆盖,故对于同一个共用体型变量,给一个新的成员赋值就“冲掉”了原有成员的值。因此在引用变量时应十分注意当前存放在共用体型变量中的究竟是哪个成员。7.5.4 使用共用体应注意的问题(2) 共用体型变量的地址和它的各成员的地址同值,如上述共用体型变量a在内存中存放的情形如图7-9所示,所以&a.c,&a.i,&a.f,&a都是同一地址值。(3) 不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,又不能在定义不能在定义共用体型变量时对其初始化。即union data a ,0,1.25是错误的。不能把共用体型变量作为函数参数或函数的返回值,但可以使用指向共用体型变量的指针(与结构体型变量的用法类似)。共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。例7-20 根据类型标志typeflag可以处理任意类型的结构体成员变量。#include main() struct sss union uuu int i; char c; float f; double d; data; char type_flag num;printf(Input the numbers type(i,c,f,d):n);num.type_flag=getche();printf(Input the numbern”);switch(num.type_flag)case i:scanf(%d,&num.data.i);break; case c:scanf(%c”,&num.data.c);break; case f:scanf(%f,&num.data.f);break; case d:scanf(%lf,&num.data.d);switch(num.type_flag)case i:printf(%d,num.data.i);break; case c:printf (%c,num.data.c);break; case f:printf (%f,num.data.f);break; case d:printf (%lf,num.data.d);7.6 位段7.6.1 位段的概念7.6.2 使用位段应注意的问题 在实际应用中,经常会遇到某个变量的取值是一个固定位数的字符串,且每位取值是有一定范围的。如某学校的学生学号共8位,第1,2位是入学年份的后两位数字(00-99),第3,4位是所在系的代号(00-15),第5位是所学专业的代号(0-9),第6位是本专业的班号(0-7)第7,8位是所在班的序号(1-30),通常的作法是每个变量用长度为8的字符数组来存储(占8个字节)。但是如果从二进制位(bit)角度来考虑,23 个bit就足够了(用7个bit表示年份,用4个bit表示系号,用47.6.1 位段的概念个bit表示专业代号,用3个bit表示班号,用5个bit表示班内序号),可以用4个字节来存储,这样可节省一半的存储空间。C语言能否实现上述目的吗?能! 我们知道,内存存取的最小单位是字节,即不能直接对字节中的若干位进行存取,但是可以利用C语言的位运算功能实现上述目的即先取出1个或若干连续字节的值,在用位运算得到其某些连续位上的值,但比较杂,请读者与阅读有关位运算的内容,在此不再展开讨论。另一个方法就是利用C语言的位段的概念。位段是一种特殊的结构体类型,其每个成员是以位为单位来定义长度的,不再是各种类型的变量。例如,学生学号可以定义为:struct packed_xh unsigned nf:7; unsigned xb:4; unsigned zy:4; unsigned bj:3; unsigned sx:5; xh; 该结构体变量xh在内存中占3个字节,每个位段分别占7,4,4,3,5位。见图7-10。注意,在存储单元中段位的空间分配方向,因机器而异,有些是从左到右,有些是从右到左。 对位段中的数据的饮用的方法,与结构体一致。如 xh.nf=3; xh.xb=8; xh.zy=2; xh.bj=3; xh.sx=23;但应注意位段允许的最大值范围。如令xh.bj=9系统只将1001后位001赋给了xh.bj,xh.bj的到的值是1.(1) 一个位段必须被说明成int,unsigned或signed中的任一种。长度为1的位段被认为是unsigned类型,因为单个位不可能具有符号。(2) 位段中成员的引用与结构体类型中成员的引用一样,用“”运算符。比如:xh.xb=8表示引用位段变量xh中的第2位,它的值是015中的一个。(3) 可定义无名位段,如:7.6.2 使用位段应注意的问题struct unsigned a:1 unsigned b:2 unsigned :5 unsigned c:8 (4)一个位段必须存贮在同一存贮单元中,不能跨两个单元,所以位段总长不能超过整型长度的边界。(5)位段可在表达式中被引用(按整型数),也可用整型格式符输出。7.7 枚举类型7.7.1 枚举类型与 枚举类型变量的定义7.7.2 枚举类型变量 在使用中的几点说明 如果一个变量只有几种可能的值,可以定义为枚举类型。所谓“枚举”是指将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。 声明枚举类型用enum开头。例如:enum weekday sun,mon,tue,wed,thu,fri,sat;声明了一个枚举类型enum weekday,可以用此类型来定义变量。如 enum weekday workday, week_end;定义workday和week_end为枚举类型变量,它们的值只能是sun到sat之一。如 workday=mon; week_end=sun;是正确的。7.7.1 枚举类型与枚举 类型变量的定义当然,也可以在声明枚举类型的同时定义枚举变量,如enum weekday sun,mon,tue,wed,thu,fri,sat workday, week_end;其中sun、mon、sat等称为枚举元素或枚举常量。它们是用户定义的标识符。这些标识符并不自动地代表什么含义。例如,不因为写成sun,就自动代表“星期天”。(1) 在C编译系统中,对枚举元素按常量处理,故称枚举常量。它们不是变量,不能对它们赋值。(2) 枚举元素作为常量,它们是有值的,C语言编译按定义时的顺序使它们的值为0,1,。如在上面的定义中,sun=0,mon=1, ,sat=6。也可在定义是改变枚举元素的默认值,如 enum weekday sun=7,mon=1,tue,wed,thu,fri,sat workday, week_end; 7.7.2 枚举类型变量 在使用中的几点说明
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
提示  人人文库网所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
关于本文
本文标题:大学实用C语言程序设计教程-陈建铎-大学教学资料课件PPT
链接地址:https://www.renrendoc.com/p-21835837.html

官方联系方式

2:不支持迅雷下载,请使用浏览器下载   
3:不支持QQ浏览器下载,请用其他浏览器   
4:下载后的文档和图纸-无水印   
5:文档经过压缩,下载后原文更清晰   
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

网站客服QQ:2881952447     

copyright@ 2020-2025  renrendoc.com 人人文库版权所有   联系电话:400-852-1180

备案号:蜀ICP备2022000484号-2       经营许可证: 川B2-20220663       公网安备川公网安备: 51019002004831号

本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知人人文库网,我们立即给予删除!