380 likes | 476 Views
第11单元 * 结构体与共用体 * 位运算. 学习要求. 一般了解结构体变量的定义、引用 一般了解共用体变量的定义、引用 一般了解枚举类型 一般了解自定义类型 一般了解位运算符和位运算. 问题提出. 管理学生档案:假设每个学生信息包括学号、姓名、性别、年龄、成绩、地址。. 这些信息是一个整体,相互联系。在管理时如果以学生为一个管理单位,将这些数据组合在一起,将便于引用。这样的数据结构称为 “ 结构体 ” 。在其他高级语言中称为 “ 记录 ” 。. 解决办法. struct student{ // 声明结构体 struct student
E N D
学习要求 • 一般了解结构体变量的定义、引用 • 一般了解共用体变量的定义、引用 • 一般了解枚举类型 • 一般了解自定义类型 • 一般了解位运算符和位运算
问题提出 • 管理学生档案:假设每个学生信息包括学号、姓名、性别、年龄、成绩、地址。 • 这些信息是一个整体,相互联系。在管理时如果以学生为一个管理单位,将这些数据组合在一起,将便于引用。这样的数据结构称为“结构体”。在其他高级语言中称为“记录” 。
解决办法 struct student{ //声明结构体struct student int num; char name[20]; char sex; int age; float score; char addr[40]; }; struct student stu1={10010,"Liping",'M',19,87.5,"Bejing"};//定义结构体变量 printf(“%s,%s”,stu1.name,stu1.addr); //引用结构体变量的成员
概述 • 基本数据类型包括整型、实型、字符型。 • 数组属于构造类型数据,数组中各元素属于同一基本类型。 • 结构体就是将各种类型的数据组合成一个有机的整体。结构体的成员可以是不同的数据类型。
声明结构体 • 形式: • struct 结构体名{ • 成员表列 • }; • 说明: • 成员表列的每个成员都应进行类型说明 • 格式:类型名 成员名; 分号不能少!
例:声明一个日期结构体 • 日期包含年月日,可以这样声明 struct date{ int year; int month; int day; };
还可以直接定义,不出现结构体名 定义结构体变量 • 先声明结构体,再定义结构体变量。如: struct student stu1,stu2; struct date birthday; • 在声明结构体的同时定义变量。如: struct date{ int year; int month; int day; }birthday; struct student{ int num; char name[20]; char sex; int age; float score; char addr[40]; }stu1,stu2;
结构体类型的几点说明 • 类型与变量是两个概念。在编译时类型不分配空间,只对变量分配空间,可以对变量进行读写。 • 结构体中的成员,又称为域(field),可以单独使用。地位与变量相当。引用方法: • 结构体变量名.成员名 • 例:birthday.month • 例:stu1.age
结构体成员可以是结构体 struct date{ int year; int month; int day; }; struct student{ int num; char name[20]; char sex; struct date birthday; float score; char addr[40]; }stu1,stu2; struct student结构 引用name: stu1.name 引用day: stu1.birthday.day
结构体变量的引用 • 只能引用结构体变量的成员,与变量一样使用(赋值、计算、输出、求地址)。不能整体引用结构体变量。 • stu1.num=10010; • scanf(“%s”,stu1.name); • sum=stu1.score+stu2.score; • stu1.age++; • printf(“%c”,stu1.sex); • struct student *p=&stu1; //stu1首地址 • char *q=&stu1.name; //stu1成员name的首地址
结构体变量的初始化 • 可以象变量或数组一样,在定义时初始化。 void main() { struct student{ int num; char name[20]; char sex; int age; float score; char addr[40]; } stu1={10010,"Lining",'M',19,87.5,"Bejing"}; ; struct student stu1={10010,"Liping",'M',19,87.5,"Bejing"};
结构体数组 • 一个结构体变量可以放一组数据(一个学生的信息);若干个学生的信息可以用结构体数组存放。 请思考如何定义数组、初始化、输入、引用。 编程求平均成绩、输出成绩最好的学生信息。
结构体数组的定义和引用 • 声明结构体同时定义数组: • struct student {……} stu[10]; • 先声明结构体,再定义结构体数组: • struct student {……}; • struct student stu[10]; • 可以定义并同时初始化: • struct student stu[10]={{10010,"Liping",'M',19,87.5,"Bejing"},{},……}; • 引用: • stu[i].score
p p++ *指向结构体指针 struct student • 结构体变量的指针(地址)就是该结构体变量所占据的内存段的起始地址。可以通过指针变量指向结构体变量。 • 注意结构体指针的+1运算指针怎么移动?
*学生例题改用指针解决 • 定义一个指向结构体的指针变量: • sturct student *p; • 指向数组: • p=stu; • 引用: • (*p).score //注意必须加括号 • 指针移动: • p++
*几点说明 • 定义指向结构体的指针变量p,可以将结构体变量的起始地址赋给p。 • (*p)指向结构体变量,(*p).成员名指向结构体变量的成员。注意:必须加括号。*p.成员名相当于*(p.成员名)。 • 指向成员也可用形式: • p->成员名 • 结构体变量.成员名== (*p).成员名==p->成员名 等效 等效
例题:投票统计 • 有3个人,张三,李四,王五,10个人参加投票,得票最高的当选。 • 声明一个结构体,包括姓名和票数两个成员 • struct person{ • char name[20]; • int count; • }; • 定义一个struct person的数组,三个数组元素 • struct person leader[3]={{“张三”,0}, {“李四”,0},{“王五”,0}}; • 用循环进行投票(输入姓名),统计相应得票数 • 找出得票最多的那个人
**共用体 • 使几个不同的变量共同占用一段内存地址的结构称为“共用体”类型的结构。 • 定义共用体类型变量的形式: • union 共用体名{ • 成员表列 • }变量表列; • 引用共用体变量: • 共用体变量名.成员名
**共用体 引用: a.i=10; printf(“%c”,a.ch); 不能整体引用共用体变量 定义: union data{ int i; char ch; float f; }a,b,c; 或定义好union data后,再 union data a,b,c;
**共用体与结构体区别 • 共用体所占内存字节数是最大的成员的字节数;结构体所占内存字节数是所有成员的字节数和。 • 共用体某个时刻只有一个成员在起作用;结构体每个成员都有自己的空间,各自独立存在。 • a.i=10; • a.ch=‘a’; • a.f=1.5; //前面两条语句失去意义,a.i和a.ch不能再引用 • 共用体所有成员的地址相同;结构体每个成员地址不同。 • 不能把共用体变量作为函数参数,结构体可以。
**枚举类型 • 如果一个变量只有几种可能,可以定义为枚举类型。 • “枚举”:将变量的值一一列举出来。变量的值只限于列举出来的值。 • 声明枚举类型用enum开头。 • 例:enum weekday {sun,mon,tue,wed,thu,fri,sat}; • 对于编译系统,将枚举元素作为常量处理,按定义顺序使它们的值为0,1,…… • 定义枚举类型的变量: • enum weekday workday; • workday=mon; • printf(“%d”,workday); //打印出1
**用typedef定义类型 • 用typedef声明新的类型名来代替已有的类型名。 • 例: • typedef int INTEGER; • typedef float REAL; • INTEGER a,b; //等效 int a,b; • REAL x,y; //等效 float x,y; • typedef并不创建新的类型,只是创建了便于使用的标签。 • typedef与#define不同:typedef解释由编译器,#define是预处理。 typedef给出的符号仅限于类型,#define是文本替代。
*位运算 • 位运算是指对二进制位的运算。 • 系统软件的编写、计算机的检测和控制都要用到位运算。 • 可以向远程设备发送一两个字节来控制该设备,其中的每一位都有特定的含义。 • 可以使用代表特定项目的特定位来存储操作系统的关于文件的信息。 • 许多压缩和加密操作都对单独的位进行操作。
00000011 & 00000101 00000001 *位运算实例 计算:3&5 =1 可以利用按位与运算取二进制的某些(个)位: 设计一个数,将需要保留的这些位上置1,其余位置0,进行按位与运算。 思考:有个数01010101(84),想保留左起1、2、4位? 实例: IP地址:10.11.168.69 子网掩码:255.255.255.0 进行位与运算,找出网段
00000011 | 00000101 00000111 *位运算实例 计算:3|5 =7 可以利用按位或运算使二进制的某些(个)位置1: 设计一个数,将需要置1的这些位上置1,其余位置0,进行按位或运算。 思考:有个数00110000,将高4位保留,低4位全部置1?
~ 00000011 11111100 *位运算实例 计算:~3 = -4 思考:要使某个整数a的最低位为0,其余各位保留。怎么实现?(要保证算式对于2字节或4字节系统都正确) 对于2字节:a&0177776 对于4字节:a&037777777776 统一:a&~1
00000011 ^ 00000101 00000110 *位运算实例 可以利用按位异或运算,使特定位翻转(1变0,0变1): 设计一个数,将需要翻转的位置1,其余置0,进行按位异或运算。 思考:将01111010低4位翻转? 计算:3^5 = 6 思考:假设 int a=3,b=4; 执行:a=a^b;b=b^a;a=a^b; 问a,b值? 前2:b=b^(a^b)=b^b^a=0^a=a 第3:a=a^b^a=a^a^b=0^b=b 不要中间变量交换两个变量的值
*位运算实例 • a=15,计算a<<2=? • 00001111左移2位=00111100,答案为60。 • 左移1位运算相当于乘以2 • a=64,计算a>>2=? • 01000000右移2位=00010000,答案为16。 • 右移1位运算相当于除以2
*位运算实例 • 取一个整数右端开始的4~7位。 • 右移4位,使这4位为最低4位; • 取最低4位为1、其余各位为0的数(表示为~(~0<<4)) ,进行按位与运算;
*位运算实例 • 循环移位:将16位整数a右循环移动n位。 • 将a右端n位放到b中的高n位(a左移16-n位); • 将a右移n位; • 将a与b进行按位或运算; a
实验指导 • 定义一个结构体变量(包括年、月、日)。输入一个日期,计算该日在本年中是第几天? • 提示: • 用数组表示每个月的天数 • int a[12]={31,28,31,30,31,30,31,31,30,31,30,31}; • 注意闰年的判断:如果是闰年,a[1]=29; • 计算第几天,将这个月之前的月份天数相加再加上日子。
实验指导 • 设计一个函数print,输出一个学生的成绩数组,该数组中有3个学生的数据记录,每个记录包括编号num、姓名name、成绩数组score[3],用主函数输入这些记录,用print函数输出这些记录。 • 定义结构体:必须在程序开始的位置定义,不能在main中定义 struct student{ int num; char name[20]; int score[3]; }; • 定义函数: void print(struct student p){ printf("%d,%s,%d,%d,%d\n",p.num,p.name,p.score[0], p.score[1],p.score[2]); }
实验指导 • 有3个学生,每个学生的数据包括学号、姓名、3门课程的成绩。从键盘输入这3个学生的数据,然后打印出3门课程的总平均成绩,以及最高分的学生的数据。 • 在上一题的基础上完成。 • 引用分数: • stu[i].score[k] //表示第i个学生的第k门课程成绩 • 计算每人的平均分 • 找最高平均分是谁
**实验指导 • 实现从一个整数a中取出自右端开始的n~m位。 • 选做:参考课件。
**实验指导 • 实现对一个整数进行左右循环移位。要求要循环位移的数a是一个八进制数,同时,移动位数n小于0时表示左移;大于0时表示右移。 • 选做:参考课件。
**实验指导 • 实现在任意给出一个数的原码时,能够得到该数的补码。 • 选做。 • 提示: • 正数补码:与原码相同 • 负数补码:高位外取反+1