第九章 系 统 安 全 性 9.1 结构体 9.2 结构体型数组  9.3 结构体型指针 9.4 内存的动态分配 9.5 共用体

Slides:



Advertisements
Similar presentations
电子成绩单项目实现.
Advertisements

第10章 结构体与链表 本章要点: 结构体类型与结构体变量的定义 结构体变量的引用与初始化 结构体数组 链表处理 共用体类型和枚举类型
Loops.
第一章 C语言概述 计算机公共教学部.
補充: Input from a text file
第二章 线性表.
C语言基础——指针的高级应用 Week 05.
数据结构与算法 数据结构与算法实验
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
第3章 栈和队列 3.1 栈 3.2 队列 3.3 应用.
第2章 线性表 线性结构 是一个数据元素的有序集合。.
第7章 结构体、联合体和枚举类型 本章导读 本章主要知识点 《 C语言程序设计》 (Visual C++ 6.0环境)
第一章 程序设计入门.
C程序设计 第9章 自定义数据类型 主讲教师: 鲁 萍 西安建筑科技大学 理学院.
C 程序设计实例 1. 问题描述 2. 数据结构 3. 算法分析 4. 参考程序 5. 改进说明.
循环结构又称为重复结构:用来处理需要重复处理的问题,它是程序中一种很重要的结构。
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
编译原理与技术 类型检查 2018/11/21 《编译原理与技术》-类型检查.
结构体和共用体 2 梁春燕 华电信息管理教研室.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
Chap 9 结构 9.1 构建手机通讯录 9.2 结构变量 9.3 结构数组 9.4 结构指针.
程序讲解 第一题: 将指定文件的m行到n行字符写到显示屏上,m和n值从键盘输入。 运行时输入及结果: please enter m,n:
Introduction to the C Programming Language
STRUCTURE 授課:ANT 日期:2010/5/12.
C++语言程序设计 C++语言程序设计 第四章 数组及自定义数据类型 C++语言程序设计.
第九章 结构体和共用体 结构体的定义 结构体的使用 共用体的定义 共用体的使用 主讲:李祥 时间:2015年10月.
计算概论 第十八讲 C语言高级编程 结构与习题课 北京大学信息学院.
Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*
C语言程序设计 李祥.
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第五章 指针 5.1 指针的概念和定义 5.2 指针运算 5.3 指针和数组 5.4 字符串指针 5.5 指针数组 5.6 指向指针的指针
作弊是否很有诱惑性? 上堂课已经讲了 作业不一定在两个小时里都能完成 答疑没有一个人? 作弊是有记录的 心理系很多同学集体作弊,让人震惊
第3章 堆栈和队列 堆栈 堆栈应用 队列 队列应用 优先级队列 主要知识点.
C语言 程序设计基础与试验 刘新国、2012年秋.
THE C PROGRAMMING LANGUAGE
第13章 结构体的应用 13.1 了解由用户构造的数据类型 13.2 结构体类型说明及结构体变量 13.3 结构体数组
第八章 使用指针.
計數式重複敘述 for 迴圈 P
2.1 C语言的数据类型 2.2 常量与变量 2.3 变量赋初值 2.4 各类数值型数据间的混合运算 2.5 C语言的运算符和表达式
第5讲 结构化程序设计(Part II) 周水庚 2018年10月11日.
第七章 函数及变量存贮类型 7.1 函数基础与C程序结构 7.2 函数的定义和声明 7.3 函数的调用 7.4 函数的嵌套与递归
第4章 顺序程序设计.
Struct結構 迴圈
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
第十章 用户自定义数据类型 目录 学生信息管理系统的开发 结构体数据类型的概述 结构体变量的使用 结构体数组
OOP6 結構Struct 黃兆武.
目录 9.1 结构体类型 9.2 共用体类型 9.3 枚举类型 9.4 类型声明符typedef 1.
第十章 结构体与链表 西安工程大学.
C语言概述 第一章.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
浙江长征职业技术学院—计算机与信息技术系—相方莉制作
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
C程序设计.
C语言程序设计 李祥 QQ:
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第二章 类型、对象、运算符和表达式.
第三章 基本的輸出與輸入函數 (Basic Output & Input Function)
#include <iostream.h>
Introduction to the C Programming Language
第4章 鏈結串列(Linked Lists) 4-1 動態記憶體配置-(6) 4-2 鏈結串列的基礎-(7)
結構、檔案處理(Structure, File)
第18讲 从C到C++ 计算机与通信工程学院.
C/C++基礎程式設計班 C語言入門、變數、基本處理與輸入輸出 講師:林業峻 CSIE, NTU 3/7, 2015.
C/C++基礎程式設計班 陣列 講師:林業峻 CSIE, NTU 3/14, 2015.
Chap 10 函数与程序结构 10.1 圆形体积计算器 10.2 汉诺塔问题 10.3 长度单位转换 10.4 大程序构成.
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
安排座位.
Presentation transcript:

第九章 系 统 安 全 性 9.1 结构体 9.2 结构体型数组  9.3 结构体型指针 9.4 内存的动态分配 9.5 共用体 第九章 系 统 安 全 性 9.1 结构体 9.2 结构体型数组  9.3 结构体型指针 9.4 内存的动态分配 9.5 共用体 9.6 位段 9.7 用typedef定义类型

9.1 结 构 体 9.1.1 结构体类型 数组将若干具有共同类型特征的数据组合在了一起。然而,在实际处理中,待处理的信息往往是由多种类型组成的,如有关学生的数据,不仅有学习成绩, 还应包括诸如学号(长整型)、姓名(字符串类型)、性别(字符型)、出生日期(字符串型)等。 再如编写工人管理程序时,所处理对象——工人的信息类似于学生,只是将学习成绩换成工资。就目前所学知识,我们只能将各个项定义成互相独立的简单变量或数组,无法反映它们之间的内在联系。应该有一种新的类型, 就像数组将多个同类型数据组合在一起一样, 能将这些具有内在联系的不同类型的数据组合在一起,C语言提供了“结构体”类型来完成这一任务。

9.1.2 结构体类型的定义 结构体类型的定义形式如下:  struct 结构体类型名  {成员列表};  例如: struct student  {long int num;  char name[20];  char sex;  int age;  };

说明:  (1) 关键字struct和结构体类型名student组合成一种类型标识符, 其地位如同通常的int ,char 等,其用途是用来定义该结构体型变量,定义了变量之后,该变量就可以像其它变量一样的使用了,类型名便不应再在程序中出现(求长度运算除外,一般程序只对变量操作)。 类型名的起名规则遵从标识符。

(2) 成员列表为本结构体类型所包含的若干个成员的列表,必需用{ }括起来,并以分号结束。每个成员的形式为  类型标识符 成员名;  如例中的 long int num;  char name[20]; 等  成员(如num)又可称为成员变量,也是一种标识符, 成员的类型可以是除该结构体类型自身外,C语言允许的任何数据类型,结构体类型struct student中学号num是长整型 姓名name是字符数组、性别sex是字符型等等。成员之一还可以是其它结构体类型,此时称为结构体类型嵌套,如用生日代替上例中的年龄。可以定义结构体类型如下:

struct date { int year;  int month;  int day;  };  struct student1 { long int num;  char name[20];  char sex;  struct date birthday;  struct studentl *ps;  };

9.1.3 结构体型变量的定义 形式一, 类型、 变量分别定义: struct staff  9.1.3 结构体型变量的定义 形式一, 类型、 变量分别定义: struct staff  { char name[20]; /* 姓名 */ char department[20]; /* 部门 */ int salary; /* 工资 */ int cost; /* 扣款 */ int realsum; /* 实发工资 */ };  struct staff worker1, worker2;

形式二, 类型、 变量一起定义: struct staff  { char name[20];  char department[20];  int salary;  int cost;  int realsum;  } worker1, worker2;

形式三是形式二的简化, 省略类型名: struct  { char name[20];  char department[20];  int salary;  int cost;  int realsum;  } worker1, worker2;

图 9.1 结构体型变量在内存中的存贮形式

9.1.4 结构体型变量及其成员的引用 (1) 变量成员的引用方法(成员运算符 “.”): 如前例结构体类型struct staff下定义的两个变量worker1, worker2, 二变量中的每个成员均可引用,且所引用的成员变量与其所属类型的普通变量一样可进行该类型所允许的任何运算。例如: worker1.realsum=worker1.salary-worker1.cost ;  worker2.salary=worker1.salary;  scanf(″%s″, worker1.name);  scanf(″%d″, &worker1.cost); 又如:  struct student stu1, stu2; 之后, 变量stu1, stu2成员的引用可以是:  stu2.num=stu1.num + 1 ;  stu1.age ++ ;  scanf(″%ld″, &stu1.num);

在C语言的运算符中,取成员运算符“.”优先级最高, 故以上语句均为对引用之后的成员变量进行操作。  若结构体定义是嵌套的,则只能引用最低级的成员(用若干“.”运算符,逐级引用到最低级)。 例如:在上一节定义了类型student1之后,若有 struct student1 stu3;  结构体型变量stu3的成员引用为:  stu3.birthday.year  stu3.birthday.month 不可对stu3.birthday进行操作, 因其不是最低级。

(2) 结构体型变量可以整体引用来赋值:如worker2=worker1;即将变量worker1的所有成员的值一一赋给变量worker2的各成员。但不可进行整体的输入输出, 如:  printf (″%s″, worker1);  是错误的。结构体型变量只能对逐个成员进行输入或输出。

(3) 结构体型变量占据的一片存贮单元的首地址称为该结构体型变量的地址,其每个成员占据的若干单元的首地址称为该成员的地址, 两个地址均可引用。 如: scanf(″%d″, &worker1.salary); 输入worker1.salary—— 一个成员的值。  printf(″0x%x″, &worker1); 输出worker1—— 一个结构体型变量的首地址。

9.1.5 结构体型变量的初始化 例如:已定义结构体类型如前, 则:  stu1的初始化:struct student stu1={99001, ″Wang-Wei″, ′f′, 21};  stu3的初始化:struct student1 stu3={99010, ″Liu-Ping″, ′m′, {1987, 10, 2}};  所有结构体型变量,不管是全局变量还是局部变量,自动变量还是静态变量均可如此初始化。

例 9.1 利用结构体类型,编程计算一名同学5门课的平均分。 例 9.1 利用结构体类型,编程计算一名同学5门课的平均分。 main() { struct stuscore {char name[20];  float score[5];  float average;  };  struct stuscore x={"Wang-Wei", 90.5, 85, 70, 90, 98.5};  int i;  float sum=0;  for(i=0; i<5; i++) sum+=x.score[i];  x.average=sum/5;  printf("The average score of %s is %4.1f\n", x.name, x.average);  }

9.1.6 应用举例 例 9.2 将上例改为键入任意多人的5门课的成绩,打印平均分。 #include <con.0.h> 9.1.6 应用举例 例 9.2 将上例改为键入任意多人的5门课的成绩,打印平均分。 #include <con.0.h> main() { struct stuscore {char name[20];  float score[5];  float average; } x;  int i;  float sum;  char rep;  while(1)

{ printf("\nDo you want to continue?(Y/N)");  rep=getche();  if(rep==′N′ || rep==′n′) break;  sum=0;  printf("\nInput name(as Xu-jun) and 5 scores(all depart by ): \n");  scanf("%s", x.name);  for(i=0; i<5; i++) scanf("%f", &x.score[i]);  sum+=x.score[i];  x.average=sum/5;  printf("The average score of %s is %4.1f\n", x.name, x.average); } }

运行情况为  Do you want to continue?(Y/N)y Input name(as Xu-jun) and 5 scores(all depart by): (提示) Guo-Yong 80 89.5 99 87.5 66 (输入) The average score of Guo-Yong is 84.4 (输出)  Input name(as Xu-jun) and 5 scores(all depart by): (提示) Liu-Ying 87 88 89 99 98 (输入) The average score of Liu-Ying is 92.2 (输出) Do you want to continue?(Y/N)N

9.2 结构体型数组 9.2.1 结构体型数组的定义 相似于整型数组 int a[3]。 结构体型数组的定义不同之处是要先定义好结构体类型。例如:struct student 定义如前, 然后struct student stu[3]; 就定义了一个struct student结构体型数组stu,数组元素stu[0],stu[1],stu[2]分别是三个struct student结构体型变量。又如:struct staff worker[100]; 定义了另一个结构体类型struct staff的数组worker 数组元素worker[0],worker[1],…,worker[99]分别是100个 struct staff结构体型变量。

9.2.2 结构体型数组的初始化 结构体型数组和普通数组一样可以初始化,只是每个元素的初值为由{}括起来的一组数据,初始化形式是定义数组时, 后面加上={初值列表},如:  struct student stu[2] = { {99010,″Wang-Yan″,′f′,20},  {99011,″Li-Li″,′m′,19}};

例 9.3 用结构体型数组初始化建立一工资登记表。然后键入其中一人的姓名,查询其工资情况。 #include <string.h> main() { struct staff { char name[20];  char department[20];  int salary;  int cost;  } worker[3]={{"Xu-Guo", "part1", 800, 200},  {"Wu-Xia", "part2", 1000, 300},  {"Li-Jun", "part3", 1200, 350}  };

int i;  char xname[20];  printf("\nInput the worker\′s name: ");  scanf("%s", xname);  for(i=0; i<3; i++) if(strcmp(xname, worker[i].name)==0) { printf("**** %s ****", xname);  printf("\n salary: %6d", worker[i].salary);  printf("\n cost : %6d", worker[i].cost);  printf("\n payed : %6d\n", worker[i].salary-worker[i].cost);  } }

程序运行:  Input the worker′s name: Wu-Xia (提示 输入) * * * * Wu-Xia **** (输出) salary: 1000 cost: 300 payed: 700

9.3 结构体型指针 9.3.1 指向结构体型变量的指针 指向结构体型变量的指针的定义格式是:在定义结构体型变量的同时,定义一指针。  如 struct student stu1,*p;此时编译程序只知p为一结构体类型 struct student的指针,它将存放该类变量的首地址,但并无具体值。 

在程序中执行了语句 p = &stu1之后,p才真正指向了结构体型变量stu1,如图所示。 long int num char name[20] char sex int age p→

注意以下表达式的含义(指向运算符->的优先级高于++): p->num: 得到p指向的结构体型变量中的成员num的值, 假设其值为990120。  p->num++(即(p->num)++): 得到p指向的结构体型变量中的成员num的值, 用完该值后使它加1。  ++p->num(即++(p->num)): 得到p指向的结构体型变量中的成员num的值, 使之先加1, 再使用。  从以下用三条连续的输出语句的执行结果可清楚地看到其含义: printf(″%d\n″, p->num); 输出 990120 printf(″%d\n″, p->num++); 输出 990120 printf(″%d\n″, ++p->num); 输出 990122其中的输出即为使用。

例 9.4 显示某人工资信息的程序如下: #include <string.h> main() 例 9.4 显示某人工资信息的程序如下: #include <string.h> main() { struct staff { char name[20];  char department[20];  int salary;  };  struct staff w1, *p;  p=&w1;  strcpy(w1.name, "Li-Li"); /* 某人的信息*/ strcpy((*p).department, "part1");  p->salary=1000;

printf("%s %s %d\n", w1.name, 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);  } 显示结果:  Li-Li part1 1000 Li-Li part1 1000

9.3.2 指向结构体型数组的指针 例 9.5 显示工资表。 struct staff { char name[20];  例 9.5 显示工资表。 struct staff { char name[20];  int salary;  };  main() { struct staff *p;  struct staff worker[3] ={{"Wang-Li", 600}, {"Li-Ping", 700}, {"Liu-Yuan", 800}};  for(p=worker; p<worker+3; p++) printf("%s \′s salary is %d yuan\n", p->name, p->salary);  }

程序的运行结果为  Wang-Li′s salary is 600 yuan Li-Ping′s salary is 700 yuan Liu-Yuan′s salary is 800 yuan 

图 9.2 指针与结构体型数组

对于指向结构体型数组的指针, 请注意以下表达式的含义(在p=worker后):  p->salary 、 p->salary++、 ++p->salary与前页的p->num的三种形式含义相同,均为p所指向的成员变量的值, 即对worker[0].salary的值(600)在使用前还是后加1。  对于指向数组的指针,用的更多的是指针值本身的加1移动,所以要特别注意:  (++p)->salary:先使p自加1,指向下一元素,得到worker[1].salary的值,即700。  p++->salary:先得到worker[0].salary的值,即600, 然后使p加1,指为worker[1]。  (p++)->salary: 完全同上。 括号不改变++操作符在后的操作性质。

如在例9.5的worker数组初始化,且p=worker之后,连续执行以下四条语句,输出结果为 printf(″%d\n″, p->salary); 输出 600 printf(″%d\n″, (++p)->salary); 输出 700 printf(″%d\n″, p++->salary); 输出 700 printf(″%d\n″, (p++)->salary); 输出 800

9.3.3 用结构体型指针作函数的参数 例 9.6 用子函数求出worker数组中每个工人的实发工资。 #define NUM 3 struct staff { char name[20];  char department[20];  int salary;  int cost;  int realsum;  };  main() { void getreal(struct staff *p, int n);  struct staff worker[NUM], *pm;  int i;  printf("Input %d worker\′s name department salary cost: \n",NUM);

for(i=0, pm=worker; i<NUM; i++, pm++) scanf("%s%s%d%d", pm->name, pm->department, &pm->salary, & pm->cost);  pm=worker; /* 注1 */ getreal(pm, NUM); /* 注2 */ for(pm=worker; pm<worker+NUM; pm++)  printf("%s of %s should be payed %d yuan\n", /* 注3 */ pm->name, pm->department, pm->realsum);  } void getreal( struct staff *p, int n) { int i;  for(i=0; i<n; i++, p++) p->realsum=p->salary - p->cost;  }

运行情况: Input 3 worker′s name department salary cost: (提示) Wang part1 1000 200 (输入) Liu part2 1500 300 Zhang part3 2000 600 Wang of part1 should be payed 800 yuan (输出) Liu of part2 should be payed 1200 yuan Zhang of part3 should be payed 1400 yuan

9.4 内存的动态分配 9.4.1 动态分配内存的意义 9.4.2 开辟和释放内存区的函数 1. malloc()函数 它的函数原型为 void *malloc(unsigned size);  其功能是在内存的动态存贮区中分配长度为size个字节的连续空间。  其返回值= 分配空间的起始地址 (分配成功) 空指针NULL (分配失败, 一般是没有空间)

例 9.7 分配一个能放置双精度数的空间。 2. free(p):释放由p指向的内存区,使这部分内存可以分配给其它变量 例 9.7 分配一个能放置双精度数的空间。 #include <stdlib.h> main() {double *p;  p=(double *)malloc(sizeof(double)); /* 注1 */ if(p==0) { printf("malloc error\n");  exit(0);  }  *p=78.786;  printf("*p=%f\n", *p);  } 运行结果:  *p=78.786000

另外, 对注1行有两点说明:  (1) 从malloc函数原形可以得知,其返回值为void *型, 现在是对双精度型分配空间,所以要将其返回值强行转换为double * 型。  (2) 程序中出于易于移植的考虑,使用了sizeof(double)作为malloc函数的实参。因为不同机器上的双精度所占的字节数可能不同,用这种方法不论在哪种机器上都能为双精度型数据分配大小正确的空间。

例 9.8 改进上例,在使用后释放动态分配的空间。 例 9.8 改进上例,在使用后释放动态分配的空间。 #include <stdlib.h> main() { double *p, *q;  p=(double *)malloc(sizeof(double));  if(p==0) { printf("malloc error\n");  exit(0);  } printf("p=0x%x *p=%4.1f\n", p, *p=100);  free(p);  q=(double *)malloc(sizeof(double));  if (q==0)

程序的运行结果为  p=0X4E7 *p=100.0 q=0X4E7 *q=10.0 p=0X4E7 *p=10.0 { printf("malloc error\n");  exit(0); } *q=10.;  printf("q=0x%x *q=%4.1f p=0x%x *p=%4.1f\n", q, *q, p, *p); } 程序的运行结果为  p=0X4E7 *p=100.0 q=0X4E7 *q=10.0 p=0X4E7 *p=10.0

指针p、q均为相同的地址值(具体值可能不是0X4E7),表明已经释放的由指针p所指的空间又重新分配给了指针q。 由于指针p的内容没变,故而指针p、q都指向同一空间。从第二行的结果可验证之。  在多次调用malloc函数开辟内存空间的过程中, 可能有另一种动态变化的数据也要在此分配空间,或者前边已分配的某个空间已被释放,又可重新被分配。因此多次开辟的内存空间的地址是不连续的。 这一点与数组完全不同。

另外两个相关函数是calloc()及realloc() 其原型为 void *calloc(unsigned n, unsigned size); void *realloc(void *p, unsigned size); calloc()的功能是分配n个大小为size个字节的连续空间, 它实际上是用于动态数组的分配。  realloc()的功能是将p所指出的已分配的内存空间重新分配成大小为size个字节的空间。它用于改变已分配的空间的大小,可以增减单元数。

9.4.3 链表概述 图 9.3 单向链表示意图

9.4.4 建立链表 所谓建立链表即是从无到有的形成一个链表。  建立链表的思想很简单:逐个地输入各结点的数据,同时建立起各结点的关系。这种建立关系可能是正挂、倒挂或插入,下面介绍前两种:  建立链表方法一:正挂——即先建立链头, 让链头指向首先开辟并输入数据的第一个结点;然后开辟并输入第二个结点数据,将其“挂”到第一个结点之后; 接着开辟第三个结点并输入实际数据,将其“挂”在第二个结点之后……,即按输入顺序将结点“挂”接在一起。  在实际编程时, 还有些细节要考虑, 如是否为空链等。

针对9.4.1节提出的问题, 我们建立一个职工工资链表, 现定义结点类型如下: struct staff {char name[20];  int salary;  struct staff *next; }; (为了简化程序,减少了原有的数据成员项),在形成链表的过程中,首先要定义头指针,并且还需要两个工作指针p1、 p2 ,其定义如下:  struct staff *head , *p1, *p2; 

图 9.4 正向建立链表子函数流程图

具体步骤描述如下:  (1)开始时先建一个空链表: head=NULL;形如:head (2) 开辟第一个结点空间并由p1指向,即p1=(struct staff*)(malloc(LEN)); LEN为结点结构体类型staff的一个变量所占字节数。之后, 执行语句:  scanf("%s %d", p1->name, &p1->salary); 读入其有效数据,(以工资大于0为有效数据),执行head = p1; 将其挂到链头上,(如虚线所示,其后的链表图类似)。  NULL

形如: 其中worker1代表第一个工人的姓名;至于head的具体内容是什么,即p1的值是多少,由系统决定,我们无需关心。

(3) 移动p2,使其指向最后一个结点,即执行p2=p1。形如:

(4) 再开辟下一个结点的空间由p1指向,即再次执行: p1=(struct staff*)malloc(LEN);读入有效数据后,执行p2->next=p1;将其挂至链尾。

(5) 重复3、4两步,直至所读数据无效,即p2所指为真正尾结点, 此时令p2->next=NULL,建链结束。形如:

例 9.9 正向建立链表程序清单。 #include <stdlib.h> #define NULL 0 例 9.9 正向建立链表程序清单。 #include <stdlib.h> #define NULL 0 #define LEN sizeof(struct staff) struct staff { char name[20];  int salary;  struct staff *next;  };  int n;  main()

{ struct staff *creat1(); /* 二函数声明 */ void print(struct staff *p );  struct staff *head;  head=creat1(); /* 调子函数建立链表 */ print(head); /* 从头开始显示链表各结点的数据信息 */ } struct staff *creat1() { struct staff *head, *p1, *p2;  n=0;  p1=(struct staff *)malloc(LEN); /* 开辟第一结点 */ printf("Input the worker\′s name salary (salary=0 end): \n");  scanf("%s %d", p1->name, &p1->salary); /* 读入第一结点数据 */

head=NULL; /* 建空链 */ while(p1->salary>0) { n=n+1; /* 结点数加1 */ if(n==1) head=p1; /* “挂链” */ else p2->next=p1;  p2=p1; /* 移动指针p2*/ p1=(struct staff *)malloc(LEN); /* 开辟下一结点空间 */ scanf("%s %d", p1->name, &p1->salary); /* 读入数据 */ }

p2->next=NULL; /* 数据无效置链尾 */ return (head); /* 返回链头 */ } void print(struct staff *head) { struct staff *p;  p=head; /* p指向链头 */ while(p! =NULL) /* 未至链尾,则显示结点数据信息 */ { printf("%s\′s salary is %d\n", p->name, p->salary);  p=p->next; /* p后移一结点 */ }

程序运行情况:  Input the worker's name salary(salary=0 end): W1 1000  (输入) W2 2000  W3 3000  W 0  W1′s salary is 1000 (输出) W2′s salary is 2000 W3′s salary is 3000

建立链表方法二:“倒挂”——最先输入的结点作尾结点, 后输入的结点始终“挂”在链头上。此时只需要一个工作指针p1。其思路如图9 建立链表方法二:“倒挂”——最先输入的结点作尾结点, 后输入的结点始终“挂”在链头上。此时只需要一个工作指针p1。其思路如图9.5所示,建立之后的链表如图9.6(其中代表工人姓名的worker1、worker2等也代表了结点输入的顺序),程序清单如例9.10所示。

图 9.5 倒向建立链表流程图

图 9.6 “倒挂”形成的链表

例 9.10 倒向建立链表程序清单。 #include <stdlib.h> #define NULL 0 #define LEN sizeof(struct staff) struct staff { char name[20];  int salary;  struct staff *next;  };  int n;  main() { struct staff *creat2();  void print(struct staff *p);  struct staff *head;

head=creat2();  print(head);  } struct staff *creat2() { struct staff *head, *p1;  n=0;  p1=(struct staff *)malloc(LEN);  printf("Input the worker\′name salary(salary=0 end): \n");  scanf("%s %d", p1->name, &p1->salary);  head=NULL;  while(p1->salary>0) /* 建链开始 */ { n=n+1;  if(n==1)

{ head=p1; /* 第一结点挂链 */ p1->next=NULL;  } else { p1->next=head; /* 非第一结点挂链 */ head=p1;  p1=(struct staff *)malloc(LEN);  scanf("%s %d", p1->name, &p1->salary); return(head); /* 建链结束, 返回链头 */

void print(struct staff *head) { struct staff *p;  p=head;  while(p!=NULL) { printf("%s\′s salary is %d\n", p->name, p->salary);  p=p->next;  } }

程序运行情况:  Input the worker's name salary(salary=0 end): W1 1000  (输入) W2 2000  W3 3000  W 0  W3′s salary is 3000 (输出) W2′s salary is 2000 W1′s salary is 1000

9.4.5 链表的其它操作 1. 遍历输出 图 9.7 遍历示意图

2. 查找 查找思路类似遍历输出,只是将输出换成比较, 在查找到所要找的一个或全部项后结束子函数。 3. 删除 删除是将某一结点从链中摘除,即将待删结点与其前一结点解除联系。如图9.8(a)(head=p1->next) 或图9.8(b) (p2->next =p1->next)。之后顺着链头就访问不到p1结点了,即p1所指的结点被删除了,不再是链表的一部分了。

图 9.8 删除链表示意图

4. 插入 图 9.9 链表插入示意图

9.5 共 用 体 9.5.1 共用体类型 共用体与结构体定义相类似, 只是定义时将关键词struct换成union。 如定义一个共用体类型 union data: union data {int i;  char ch;  float f;  } datu;

图 9.10 共用体型变量datu在内存中的存贮情况

9.5.2 共用体型变量的引用方式 共用体型变量的引用方式与结构体类型的引用方式相同。 如共用体型变量datu的成员引用可以是 scanf(″%d″, &datu.i);  printf(″%f″, datu.f);  但其整体引用, 如:  printf(″%d″, datu);  或 datu=1; j=datu; 等都是错误的。

9.5.3 共用体型变量的特点 (1) 一个共用体型变量可以用来存放几种不同类型的成员,自然无法同时实现。即每一个瞬间只有一个成员起作用。因各成员共用一段内存,彼此互相覆盖, 故对于同一个共用体型变量,给一个新的成员赋值就“冲掉”了原有成员的值。因此在引用变量时应十分注意当前存放在共用体型变量中的究竟是哪个成员。  (2) 共用体型变量的地址和它的各成员的地址同值,如上述共用体型变量datu在内存中存放的情形如图9.10所示,所以&datu, &datu.i, &datu.ch, &datu.f 都是同一地址值。

(3) 不能在定义共用体型变量时对其初始化。 即union data datu={2, ′A′, 0

9.5.4 应用举例 例 9.11 根据类型标志type-flag可以处理任意类型的结构体成员变量。 9.5.4 应用举例 例 9.11 根据类型标志type-flag可以处理任意类型的结构体成员变量。 #include <conio.h> main() { struct { union { int i;  char ch;  float f;  double d;  } data;  char type-flag;  } num;

printf("Input the number\′s type(i, c, f, d): \n");  num.type-flag=getche();  printf("Input the number: \n");  switch(num.type-flag) { case ′i′: scanf("%d", &num.data.i); break;  case ′c′: scanf("%c", &num.data.ch); break;  case ′f′: scanf("%f", &num.data.f); break;  case ′d′: scanf("%lf", &num.data.d);  } { case ′i′: printf("%d", num.data.i); break;  case ′c′: printf("%c", num.data.ch); break;  case ′f′: printf("%f", num.data.f); break;  case ′d′: printf("%lf", num.data.d);  }

9.6 位 段 位段是一种特殊的结构体类型,其每个成员是以位为单位来定义长度的,不再是各种类型的变量。 例如: 9.6 位 段 位段是一种特殊的结构体类型,其每个成员是以位为单位来定义长度的,不再是各种类型的变量。 例如: struct packed-data { unsigned x: 1;  unsigned y: 2;  unsigned z: 3;  } bits;

z y x bits: 6 3 1 0 图 9.11 位段示意图 说明:  (1) 一个位段必须被说明成int,unsigned或signed中的任一种。 长度为1的位段被认为是unsigned类型,因为单个位不可能具有符号。

(2) 位段中成员的引用与结构体类型中成员的引用一样, 用“. ”运算符。比如:bits (2) 位段中成员的引用与结构体类型中成员的引用一样, 用“.”运算符。比如:bits.x表示引用位段变量bits中的第一位, 它的值只能是0或1,而bits.z的值可以是0~7中的任一值,而bits.z=8就错了,因为三位二进制码所能表示的数不能超过7。 若由指针来访问时,必须使用指向运算符。 (3) 可定义无名位段:  struct  {unsigned a: 1;  unsigned b: 2;  unsigned : 5; /* 此5位无名,不用 */ unsigned c: 8;  };

(4) 一个位段必须存贮在同一存贮单元中,不能跨两个单元, 所以位段总长不能超过整型长度的边界。  (5) 位段可在表达式中被引用(按整型数),也可用整型格式符输出。  (6) 位段不能定义成数组。  (7) 不同的机器内部,位段是从右到左分配,还是从左到右分配是不定的,要事先试验。任何实际位段的代码都可能与机器的特征有关。

例 9.12 #include <stdio.h> main() { union { int i;  struct { unsigned a: 1;  unsigned b: 1;  unsigned c: 2;  } bits;  }num;  printf("\nInput an integer between 0 to 15: ");  scanf("%d",&num.i);  printf("i=%3d,bits=%3d%3d%3d\n",num.i,num.bits.c,num.bits.b, num.bits.a);  }

程序运行情况:  Input an integer between 0 to 15: 7  (提示 输入) i=7, bits= 1 1 1 (输出) 再次运行:  Input an integer between 0 to 15: 4  (提示 输入) i=4, bits= 1 0 0 (输出) 注: Turbo C 是从右向左开始分配各成员的。

9.7 用typedef定义类型 typedef语句的的一般形式为  typedef 原数据类型 新的类型名;  如可以定义结构体类型如下: typedef struct  {int year;  int month;  int day;  }DATE; 

新的类型名DATE 就代替了上面的结构体类型,此后就可以用DATE 来定义该结构体型变量: DATE birthday ; (不要写成 struct DATE birthday) DATE *p; (p为指向该结构体型数据的指针)