第12章结构体、共用体和位运算12.1结构变量12.2结构数组12.3结构指针变量12.4联合类型的定义和联合变量的说明12.5联合变量的赋值和使用12.6位运算符12.7链表退出12.1结构变量“结构”也叫做“结构体”,它是由若干“成员”组成的。每一个成员可以是一个基本数据类型,还可以是数组、指针,甚至可以为另一个构造类型(此时,就组成了嵌套)。在说明和使用之前必须先定义结构,就像在说明和调用函数之前要先定义函数一样。12.1.1结构体的定义结构体有以下几种定义方法。1不带变量名的定义定义一个结构体的一般形式为:struct结构名{数据类型成员名1;数据类型成员名2;数据类型成员名3;...数据类型成员名n;};成员名的命名应符合标识符的规定。成员名可与程序中其它变量同名,互不干扰。注意不要忘记成员名后面的逗号,也不要忘记结构结束时}后的逗号。2带变量名的定义另外一种定义为:struct结构名{数据类型成员名1;数据类型成员名2;数据类型成员名3;...数据类型成员名n;}变量名列表;【例12-1】(见课本)12.1.2结构类型变量的说明说明结构变量有以下几种方法。1先定义结构,再说明结构变量说明的格式为:存储类型struct结构名结构变量名1[,结构变量名2,...结构变量名n];【例12-2】(见课本)2定义结构类型并说明结构变量如上所说,采用第二种格式,可以在定义结构类型的同时说明结构变量。12.1.3结构变量成员的表示方法在程序中使用结构变量时,往往使用它的单个成员,而不使用整个结构。成员可以在程序中单独使用,与普通变量完全相同。表示结构变量成员的一般形式是:结构变量名.成员名例如:jilu1.bianhao/*记录1的编号*/jilu2.xingbie/*记录2的性别*/这有点像数据库中的字段。12.1.4结构变量的赋值结构变量的赋值就是给各成员赋值。可用输入语句或赋值语句来完成。【例12-3】(见课本)12.1.5结构变量的初始化如果结构变量是全局变量或为静态变量,则可对它作初始化赋值。对局部或自动结构变量不能作初始化赋值。【例12-4】(见课本)【例12-5】(见课本)12.2结构数组12.2.1结构数组概述我们知道,具有相同数据类型的变量可以组成数组。同样,具有相同结构的结构变量我们知道,具有相同数据类型的变量可以组成数组。同样,具有相同结构的结构变量也可以组成数组。这样组成的数组叫做“结构数组”。结构数组的每一个元素都是具有相同结构类型的结构变量。结构数组的定义方法和结构变量相似,只需说明它为数组类型即可。说明格式如下:[存储类型]struct结构名结构数组名[元素个数];例如:structrecordjilu[3];也可以在定义时说明,如下例所示。【例12-6】(见课本)12.2.2结构数组的初始化对外部结构数组或静态结构数组可以作初始化赋值,例如:【例12-7】(见课本)当对全部元素作初始化赋值时,也可不给出数组长度。请看下例。【例12-8】(见课本)12.3结构指针变量12.3.1结构指针变量概述当把一个指针变量指向一个结构变量时,这个指针变量就成为了结构指针变量。结构指针变量指向结构变量的首地址。通过结构指针即可访问该结构变量,这与数组指针和函数指针的情况相同。结构指针变量说明的一般形式为:struct结构名*结构指针变量名例如:structrecord*pstu;也可在定义stu结构时同时说明pstu。结构指针变量也必须要先赋值后才能使用。赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。结合上述例子,可以如下赋值:pstu=&jilu而如下赋值是错误的:pstu=&record结构名与结构变量的区别是:结构名只表示一个结构形式,编译系统并不对它分配内存空间。而变量分配存储空间。因此上面&record是错误的,不可能去取一个结构名的首地址。12.3.2结构变量的使用有了结构指针变量,就能更方便地访问结构变量的各个成员。其访问的一般形式为:(*结构指针变量).成员名或为:结构指针变量-成员名例如:(*pstu).bianhao或者:pstu-bianhao应该注意(*pstu)两侧的括号不可少,因为成员符“.”的优先级高于“*”。以下三种用于表示结构成员的形式是完全等效:结构变量.成员名(*结构指针变量).成员名结构指针变量-成员名【例12-9】(见课本)【例12-10】(见课本)12.4联合类型的定义和联合变量的说明“联合体”也叫做“联合”,它也是一种由不同类型数据组成的复合数据类型。在一个“联合”内可以定义多种不同的数据类型,一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据。这在前面的各种数据类型中都是办不到的。例如,定义为整型的变量只能装入整型数据,定义为实型的变量只能赋予实型数据。“联合”与“结构”有一些相似之处,也有本质上的不同。第一点不同在于存储空间的分配:在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长成员的长度。可以使用以下通俗地比喻:结构好比每人都有自己的衣服,并且为“量体裁衣、度身定做”;而联合则是按照个子最大的人作衣服,大家都可穿这个“大”衣服。所以,从这个意义上,联合体也叫做“共同体”或“共用体”。这样,联合可以覆盖地使用内存,极大地提高了内存的使用效率。总之,联合对内存的使用可以说是“取长补短”。第二点不同在于变量的使用:联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。但是结构的变量则不可这样使用。正是这样特点,使联合比结构灵活。联合类型的定义和联合变量说明的格式与结构非常相似,只是把struct改为union而已。12.4.1联合的定义定义联合体有以下几种方式。1不带变量名的定义使用此方法定义一个联合的一般形式为:unoin联合名{数据类型成员名1;数据类型成员名2;数据类型成员名3;...数据类型成员n;};2带变量名的定义使用此方法定义一个联合的一般形式为:unoin联合名{数据类型成员名1;数据类型成员名2;数据类型成员名3;...数据类型成员n;}变量名列表;【例12-11】(见课本)12.4.2联合变量的说明一个联合类型必须经过定义之后,才能把变量说明为该联合类型。联合变量的说明和结构变量的说明方式相同,也有三种形式。即先定义,再说明;定义同时说明和直接说明。【例12-12】(见课本)【例12-13】(见课本)【例12-14】(见课本)12.5联合变量的赋值和使用12.5.1赋值对联合变量的赋值、使用都只能是对变量的成员进行。联合变量的成员表示为:联合变量名.成员名例如,a被说明为uarea类型的变量之后,可使用:a.c_dataa.s_data不允许只用联合变量名作赋值或其它操作。也不允许对联合变量作初始化赋值,赋值只能在程序中进行。还要再强调说明的是,一个联合变量,每次只能赋予一个成员值。换句话说,一个联合变量的值就是联合变员的某一个成员值。【例12-15】(见课本)12.5.2使用联合应注意的问题1内存覆盖尽管在联合所使用的内存中可以存储不同的成员,也就是可以存储不同类型的数据。但每一时刻只能存储一个成员。通俗地说,只有一套衣服,一次只能由一个演员来穿。2以新替旧联合体变量存储的是最后一次存储的成员的内容。换言之,变量存储了新成员,则自动替换了老成员。3地址唯一联合体变量和各个成员拥有同一地址。4变量不可赋值不能给联合体的变量名赋值,也不能对联合体的变量名初始化,也不能引用联合体的变量名。使用的都是联合体变量的成员。5嵌套联合与结构可以互相嵌套。数组也可以作为联合体的成员,也可以定义联合体数组。6不能用作函数不能把联合体变量作为函数的参数,也不能使函数带回联合体变量。但是,可以使用联合体变量的指针。12.6位运算符12.6.1几个基本概念1字节与位字节(英文为byte)是计算机中的存储单元。一个字节可以存放一个英文字母或符号,一个汉字通常要用两个字节来存储。每一个字节都有自己的编号,叫做“地址”。一个字节由若干位(位的英文是bit)组成。至于一个字节由几个位组成,取决于计算机的硬件系统。一般由8位、16位或32位组成,所对应的计算机也被称为“8位机、16位机或32位机”。目前微机以16位机或32位机为主。但在本书中作为原理讲述的是8位机。我们把若干字节组成一个单元,叫做“字”(英文为word)。一个字可以存放一个数据或指令。2原码我们知道,计算机使用的是二进制数。但这些数据有不同的编码方式,分别有原码、反码和补码。以8位计算机系统为例,我们把最高位(即最左面的一位)留做表示符号,其他7位表示二进制数,这种编码方式叫做原码。最高位为“0”表示正数,为“1”表示负数。例如,00000011表示+3,10000011表示-3。显然,这样可以表示的数值范围在+127到-127之间。这种表示方法有一个缺陷,数值0会出现歧义:00000000表示+0,10000000表示-0。3反码对于正数,反码与原码相同。例如,00000011表示+3。所谓“反码”是指与“原码”在表示负数时相反:符号位(最高位)为“1”表示负数。但其余位的值相反。例如,11111100表示-3。显然,这样可以表示的数值范围在+127到-127之间。这种表示方法仍然有一个缺陷,数值0会出现歧义:00000000表示+0,11111111表示-0。4补码对于正数,补码与原码相同。0的补码为00000000。这样,0的表示唯一。对于负数,可以从原码得到补码,步骤如下:把符号位不变,为1;首先,把其余各位取反。即0变为1,1变为0;其次,对整个数加1。计算机中的数据都采用补码。这样做的好处是,减法可以用加法来实现。如-3+4可以变成-3的补码与+4的补码相加。12.6.2位运算符位运算是对二进制的运算。也就是说,其操作数是二进制数。这是与其他运算的主要不同。以下逐一讲述一些主要的位运算符。1按位取反功能:把操作对象二进制数的每一位取反,0变成1,1变成0。运算符:~示例:如x=01010011,y=~x,则y=10101100。2左位移功能:把操作对象二进制数的向左移动指定的位,并在右面补上相应的0。格式:要位移的数据要位移的位数示例:如x=01010011,y=x2,则y=10110000。注意,左移会引起数据的变化,具体说,左移一位相当于对原来的数值乘以2。左移n位相当于对原来的数值乘以2n。3右位移功能:把操作对象二进制数的向右移动指定的位,并在左面补上相应的0。格式:要位移的数据要位移的位数示例:如x=01010011,y=x2,则y=00010100。注意,左移会引起数据的变化,具体说,左移一位相当于对原来的数值除以2。左移n位相当于对原来的数值除以2n。4按位与功能:当两个操作对象二进制数的相同位都为1时,结果数值的相应位为1,否则为相应位是0。运算符:&格式:数据1&数据2示例:如x=10010010,y=11011110,z=x&y,则z=10000010。5按位或功能:当两个操作对象二进制数的相同位都为0时,结果数值的相应位为0,否则为相应位是1。运算符:|格式:数据1|数据2示例:如x=10010010,y=11011110,z=x|y,则z=11111110。6按位异或功能:当两个操作对象二进制数的相同位的值相同时,结果数值的相应位为0,否则为相应位是1。运算符:^格式:数据1^数据2示例:如x=10010010,y=11011110,z=x^y,则z=01101100。12.7链表12.7.1链表概述根据前面讲述的知识,要存放较为大量的数据,可以使用数组。但是,使用数组也有一些弊端:其一,数组的大小不能变化。例如,某大学有十个系,其中计算机系的学生数最多,为800人;社会科学系的学生数最少,为300人。我们要用一个数组来存放各个系学生的相关数据,则只能按计算机系的学生数建立一个有800