当前位置: 首页 > article >正文

C语言程序设计P7【结构体和共用体】——定义和使用结构体、使用结构体数组、结构体指针、链表、共用体、枚举类型

目录

任务一:使用结构体比较学生成绩

任务二:使用结构体数组统计不及格人数

任务三:使用结构体指针求最高成绩

任务四:利用链表录入及输出学生信息

任务五:利用共用体处理学生和教师信息

任务六:利用共用体处理学生和教师信息


任务一:使用结构体比较学生成绩

知识要点:定义和使用结构体

一、任务分析

输入两个学生的学号、姓名和成绩,输出成绩较高的学生的学号、姓名和成绩(用结构体来完成)。1.定义两个结构相同的结构体变量student1和student2;

2.分别输入两个学生的学号、姓名和成绩;

3.比较两个学生的成绩,如果学生1的成绩高于学生2的成绩,则输出学生1的全部信息,如果学生2的成绩高于学生1的成绩,就输出学生2的全部信息。如果二者相等,输出两个学生的全部信息。

二、必备知识与理论

1. 结构体类型的概念

前面学习了一些基本类型(也称为简单类型),如整型、实型、字符型等,这些类型都是系统定义好的,程序员可以直接拿来定义变量。

C语言提供了自定义数据类型的方法,通过自定义类型将不同类型的数据组合成一个有机的整体,以便访问。这些数据在这个整体中是互相联系的,这种自定义的数据类型称为结构体(structure)。

如果程序要用到如表7-1所表示的数据结构,可以在程序中自己建立一个结构体类型

声明一个结构体类型的一般形式为:

struct 结构体名

{

成员表列

};

声明结构体类型时需要注意以下几个问题:

(1)结构体类型名为struct Student,其中struct是定义结构体类型的关键字,它和系统提供的基本类型具有相同的地位和作用,都是可以用来定义变量的类型。

(2)在{}中定义的变量我们叫做成员,其定义方法和前面变量定义方法一样,只是我们不能忽略最后的分号。

(3)成员可以属于另一个结构体类型。

2.结构体变量的定义

为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据,可以采取以下3种方法定义结构体类型变量。

(1)先声明结构体类型,再定义该类型的变量

我们已经申明了一个结构体类型struct Student,可以用它来定义变量。例如:

struct Student student1,student2;

(2)在声明类型的同时定义变量

(3)不指定类型名而直接定义结构体类型变量

一般形式为:

struct

{

成员表列

}变量名表列;

第三种方法与第二种方法的区别在于第三种方法中省去了结构名,而直接给出结构变量。在三种定义方法中,经常使用的是第一种方法。

说明:

①结构体类型与结构体变量是不同的概念,不要混同。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。在编译时,对类型是不分配空间的,只对变量分配空间。

②结构体类型中的成员名可以与程序中的变量名相同,但二者不代表同一对象。

③对结构体变量中的成员,可以单独使用,它的作用与地位相当于普通变量。

3.结构体变量的初使化

结构体类型与其他的基本类型一样,也可以在定义结构体变量时指定初始值,然后可以引用这个变量,例如输出它的成员的值。

4.结构体变量的引用

(1)可以引用结构体变量中成员的值,引用方式为:

结构体变量名.成员名

“.”是成员运算符,它在所有的运算符中优先级最高,因此可以将student1.num当作一个整体,相当于一个变量。

三、任务实施

任务要求用结构体来完成,也就是说完成对两个学生成绩比较大小这个功能需要定义包含学号、姓名和成绩成员的结构体。

程序代码如下:

#include <stdio.h>

void main()

{

struct Student

{ int num;    

char name[20];           

float score;

}student1,student2;

scanf("%d%s%f",&student1.num,student1.name,&student1.score);

scanf("%d%s%f",&student2.num,student2.name,&student2.score);

printf("较高成绩是:\n");

if(student1.score>student2.score)

printf("学号:%d  姓名:%s  成绩:%f\n",

student1.num,student1.name,student1.score);

else if(student1.score<student2.score)

printf("学号:%d  姓名:%s  成绩:%f\n",

student2.num,student2.name,student2.score);

else

{ printf("学号:%d  姓名:%s  成绩:%f\n",

student1.num,student1.name,student1.score);

printf("学号:%d  姓名:%s  成绩:%f\n",

student2.num,student2.name,student2.score); }

}

运行结果如下:

201363301 张三 86

201363302 王五 97

较高成绩是:

学号:201363302  姓名:王五  成绩 97.000000

用scanf函数输入结构体变量时,必须分别输入它们的成员的值,不能在scanf函数中使用结构体变量名一揽子输入全部成员的值。在成员student1.num和student1.score的前面都有地址符&,而在student1.name前面没有&,这是由于name是数组名,本身代表地址,无须再加一个&。

根据student1.score与student2.score的比较结果,输出不同的结果信息,可以发现结构体变量的好处:由于student1是一个组合项,内放有关联的一组数据,student1.score是属于student1变量的一部分,因此如果确定了student1.score是成绩较高的,则输出student1的全部信息是轻而易举的,因为它们本身是互相关联的。如果用普通变量则难以方便地实现这一目的。

任务二:使用结构体数组统计不及格人数

知识要点:使用结构体数组

一、任务分析

使用结构体数组,计算学生的平均成绩和统计不及格的人数。

1.定义结构数组student,数组中有5个元素,并作初始化赋值;

2.在main函数中用for语句逐个累加各元素的score 成员值存于s之中,如score的值小于60(不及格)即计数器C加1,循环完毕后计算平均成绩,并输出全班总分,平均分及不及格人数。

二、必备知识与理论

1.结构体数组的定义

数组的元素也可以是结构类型的。因此可以构成结构型数组。结构数组的每一个元素都是具有相同结构类型的下标结构变量。在实际应用中,经常用结构数组来表示具有相同数据结构的一个群体。如一个班的学生档案,一个车间职工的工资表等。

定义结构体数组的一般形式为:

(1)struct 结构体名

{

成员表列

}数组名[数组长度]

(2)先声明一个结构体类型(如struct Person),然后再利用此类型定义结构体数组:

结构体类型 数组名[数组长度];

如:struct Person leader[3];

对结构体数组初始化的形式是在定义数组的后面加上:={初值表列};

如:struct Person leader[3]={“Li”,0,”Zhang”,0,”Wang”,0};

【例7.2】建立同学通迅录。

#include <stdio.h>

#define NUM 3

struct mem

{

    char name[20];

    char phone[10];

};

main()

{

    struct mem man[NUM];

    int i;

    for(i=0;i<NUM;i++)

     {

printf("input name:\n");

      gets(man[i].name);

      printf("input phone:\n");

      gets(man[i].phone);

     }

    printf("name\t\t\tphone\n\n");

    for(i=0;i<NUM;i++)

      printf("%s\t\t\t%s\n",man[i].name,man[i].phone);

}

运行结果:

input name:

Li Ping

input phone:

63636253

input name:

Zhang Wei

input phone:

66666736

input name:

Wang Xin

input phone:

67893652

name           phone

Li Ping        63636253

Zhang Wei      66666736

Wang Xin       67893652

本程序中定义了一个结构mem,它有两个成员name和phone用来表示姓名和电话号码。在主函数中定义man为具有mem 类型的结构数组。在for语句中,用gets函数分别输入各个元素中两个成员的值。然后又在for语句中用printf语句输出各元素中两个成员值。

三、任务实施

计算学生的平均成绩和统计不及格的人数。

1.定义结构数组student,数组中有5个元素,并作初始化赋值;

2.在main函数中用for语句逐个累加各元素的score 成员值存于s之中,如score的值小于60(不及格)即计数器c加1,循环完毕后计算平均成绩,并输出全班总分,平均分及不及格人数。

程序代码如下:

#include <stdio.h>

struct stu

{

    int num;

    char *name;

    char sex;

    float score;

}student[5]={

          {101,"Li ping",'M',45},

          {102,"Zhang ping",'M',62.5},

          {103,"He fang",'F',92.5},

          {104,"Cheng ling",'F',87},

          {105,"Wang ming",'M',58},

        };

main()

{

    int i,c=0;

    float ave,s=0;

    for(i=0;i<5;i++)

    {

      s+=student[i].score;

      if(student[i].score<60) c+=1;

    }

    ave=s/5;

    printf("平均分=%f\n不及格人数=%d\n",ave,c);

}

运行结果如下:

平均分=69.000000

不及格人数=2

任务三:使用结构体指针求最高成绩

知识要点:结构体指针

一、任务分析

有 n 个结构体变量,内含学生学号、姓名和 3 门课程成绩,要求输出平均成绩高 的学生信息(利用结构体指针实现)。

1.将 n 个学生的数据表示为结构体数组,按照功能函数化的思想,分别用 3 个函数来实现不同的功能: ① 用 input()函数来输入数据和求各学生的平均成绩。 ② 用 max()函数来找平均成绩高的学生。 ③ 用 print()函数来输出成绩高学生的信息。

2.在主函数中先后调用上述 3 个函数,用指向结构体变量的指针作为实参,得到结果。

二、必备知识与理论

1.指向结构体变量的指针

一个指针变量用来指向一个结构体变量时,称之为结构体指针变量。结构体指针变 量中的值是所指向的结构体变量的首地址。通过结构体指针即可访问该结构体变量,这 与数组指针和函数指针的情况是相同的。 结构指针变量说明的一般形式为:  

struct 结构名 *结构指针变量名;  

例如,在前面的例题中定义了 Student 结构体,如要说明一个指向 Student 的指针变 量 pstu,可写为:struct Student *pstu;  

当然也可在定义 Student 结构体时说明 pstu。与前面讨论的各类指针变量相同,结 构体指针变量也必须要先赋值才能使用。  赋值是把结构体变量的首地址赋予该指针变量,不能把结构体名赋予该指针变量。 如果 student1 是被说明为 Student 类型的结构体变量,则:  

pstu=&student1;  

是正确的,而  

pstu=&Student;  

是错误的。 结构体名和结构体变量是两个不同的概念,不能混淆。结构体名只能表示一个结构 形式,编译系统并不为它分配内存空间。只有当某变量被说明为这种类型的结构时,才 对该变量分配存储空间。因此&Student 这种写法是错误的,不可能去取一个结构体名的 首地址。有了结构体指针变量,就能更方便地访问结构变量的各个成员。 其访问的一般形式为:  

(*结构体指针变量).成员名 或 结构体指针变量->成员名

例如:(*pstu).num 或pstu->num

注意(*pstu)两侧的括号不可少,因为成员符“.”的优先级高于“*”。如去掉括号写作*pstu.num,则等效于*(pstu.num),这样,意义就完全不对了。

下面通过例子来说明结构体指针变量的具体说明和使用方法。

【例 7.3】通过指向结构体变量的指针变量输出结构体变量中成员的信息。  

#include <stdio.h>

struct stu

{   int num;

    char *name;

    char sex;

    float score;

 }  boy1={102,"Zhang ping",'M',78.5},*pstu;

 main()

 {  pstu=&boy1;

    printf("Number=%d\nName=%s\n",boy1.num,boy1.name);     printf("Sex=%c\nScore=%f\n\n",boy1.sex,boy1.score);     printf("Number=%d\nName=%s\n",(*pstu).num,(*pstu).name);     printf("Sex=%c\nScore=%f\n\n",(*pstu).sex,(*pstu).score);     printf("Number=%d\nName=%s\n",pstu->num,pstu->name);     printf("Sex=%c\nScore=%f\n\n",pstu->sex,pstu->score); }  

2.指向结构体数组的指针

结构体指针变量不但可以指向一个结构体变量,还可以指向结构体数组,此时指针 变量的值就是结构体数组的首地址。 结构体指针变量也可以指向结构体数组中的元素,这时指针变量的值就是该结构体 数组元素的首地址。例如定义一个结构体数组 student[5],使用结构体指针指向该数组:  

struct Student *pstu; pstu=student;

注意:由于数组不使用下标时表示的是数组的第一个元素的地址,所以指针指向数组的首地址。如果想利用指针指向第5个元素,则在数组名后附加下标,然后在数组名前使用取地址符号&,例如:pstu=&student[4];

【例 7.4】使用结构体指针变量指向结构体数组。  

#include <stdio.h>

struct stu

{     

int num;     

char *name;     

char sex;     

float score;

}student[5]={ {101,"Li ping",'M',45},

              {102,"Zhang ping",'M',62.5},

{103,"He fang",'F',92.5},

{104,"Cheng ling",'F',87},

{105,"Wang ming",'M',58},};

 main()

{  struct stu *pstu;

   pstu=student;

   for(int i=0;i<5;i++,pstu++)

     {

printf("Number=%d\nName=%s\nSex=%c\nScore=%f\n",pstu->num,                 pstu->name,pstu->sex,pstu->score);    

}   

}  

利用 for 语句,对数组进行循环操作。在循环语句中,pstu 刚开始指向数组的首地 址,也就是第一个元素的地址,因此使用 pstu->引用的是第一个元素的成员。当第一次 循环结束之后,循环变量进行自加操作,同时 pstu 也执行自加操作。

3.结构体作为函数参数

函数是有参数的,结构体变量的值可以作为一个函数的参数,使用结构体作为函数 的参数有 3 种形式:使用结构体变量作为函数的参数;使用结构体变量的成员作为函数 的参数;使用指向结构体变量的指针作为函数的参数。

【例 7.5】使用结构体变量作为函数的参数。  

#include <stdio.h>

struct stu

{    int num;

     char *name;

     float score[3];

 }student={101,"Li ping",78,89,96};

 void display(struct stu student)

 { printf("Number=%d\nName=%s\nScore[0]=%.2f\nScore[1]=%.2f\nScore[2]=%.2f\n",

student.num,student.name, student.score[0],student.score[1],student.score[2]);

}

main()

{     

display(student);

 }  

运行结果:  

Number=101

Name=Li ping

Score[0]=78.00

Score[1]=89.00

Score[2]=96.00  

使用结构体变量作为函数的实参时,采取的是“值传递”,会将结构体变量所占内 存单元的内容全部顺序传递给形参,形参也必须是同类型的结构体变量。特别是成员为 数组时将会使传送的时间和空间开销很大,严重降低了程序的效率。因此好的办法就 是使用指针,即用指针变量作函数参数进行传送。这时由实参传向形参的只是地址,从 而减少了时间和空间的开销。

【例 7.6】使用结构体变量指针作为函数的参数。

本实例对例 7.5 做了一些小的改动,使用结构体变量的指针作为函数的参数,并且在参数中改动结构体成员的数据。  

#include <stdio.h>

struct stu

{     

int num;

 char *name;

 float score[3];

}student={101,"Li ping",78,89,96};

void display(struct stu* pstudent)

{     

printf("Number=%d\nName=%s\nScore[0]=%.2f\nScore[1]=%.2f\n  Score[2]=%.2f\n",

pstudent->num,pstudent->name,pstudent->score[0],pstudent->score[1],pstudent->score[2]);

pstudent->score[1]=57;

}

main()

{   

struct stu *pstu;     

pstu=&student;     

display(pstu);     

printf("修改后成绩\nscore[1]=%.2f\n",pstu->score[1]);

}  

注意:由于传递的是变量的地址,如果在函数中改变成员中的数据,那么返回函数的时候变量会发生改变。

三、任务实施

1.将n个学生的数据表示为结构体数组,按照功能函数化的思想,分别用3个函数来实现不同的功能:

①用input函数来输入数据和求各学生的平均成绩。

②用max函数来找平均成绩最高的学生。

③用print函数来输出成绩最高学生的信息。

2.在主函数中先后调用上述3个函数,用指向结构体变量的指针作为实参,最后得到结果。

程序代码如下:

#include <stdio.h>

#define N 3

struct Student

{

int num;

char name[20];

float score[3];

float aver;

};

void main()

{

void input(struct Student stu[]);

struct Student max(struct Student stu[]);

void print(struct Student stu);

struct Student stu[N],*p=stu;

input(p);

print(max(p));

}

void input(struct Student stu[])

{

int i;

printf("录入学生的学号、姓名、三门课成绩\n");

for(i=0;i<N;i++)

{

scanf("%d %s %f %f %f",&stu[i].num,stu[i].name,

&stu[i].score[0],&stu[i].score[1],&stu[i].score[2]);

stu[i].aver=(stu[i].score[0]+stu[i].score[1]+stu[i].score[2])/3;

}

}

struct Student max(struct Student stu[])

{

int i,m=0;

for(i=0;i<N;i++)

if(stu[i].aver>stu[m].aver) m=i;

return stu[m];

}

void print(struct Student stud)

{

printf("\n成绩最高的学生是:\n");

printf("学号:%d\n姓名:%s\n三门成绩:%5.1f,%5.1f,%5.1f\n平均成绩:%6.2f\n",stud.num,stud.name,stud.score[0],stud.score[1],stud.score[2],stud.aver);

}

运行结果如下:

录入学生的学号、姓名、三门课成绩

2013633001 Liyi 66 70 90

2013633002 Wangwu 80 87 78

2013633003 Zhaoliu 90 87 85

成绩最高的学生是:

学号:2013633003

姓名:Zhaoliu

三门成绩:90.0,87.0,85.0

平均成绩:87.33

程序分析:

(1)调用input函数时,实参是指针变量p,形参是结构体数组,传递的是结构体元素地址,函数无返回值。

(2)调用max函数时,实参是指针变量p,形参是结构体数组,传递的是结构体元素的地址,函数的返回值是结构体类型数据。

(3)调用print函数时,实参是结构体变量,形参是结构体变量,传递的是结构体变量中各成员的值,函数无返回值。

任务四:利用链表录入及输出学生信息

知识要点:链表

一、任务分析

利用链表录入及输出学生的信息。

1.声明一个结构体类型,其成员包括num(学号),(score(成绩),next(指针变量)。

2.将第1个结点的起始地址赋给头指针head,将第2 个结点的起始地址赋给第1个结点的next成员,第3个成员的起始地址赋给第2个结点的next成员,以此类推,将最后一个结点的next成员赋予NULL,形成链表。

3.从头指针开始,设一个指针变量p,先指向第1个结点,输出p所指的结点,然后使p后移一个结点,再输出,直至链表的尾结点。

二、必备知识与理论

1链表的概念

链表是一种常见的重要的数据结构,它是动态进行存储单元分配的一种结构。每一次分配一块空间可用来存放一个学生的数据,我们可称之为一个结点。有多少个学生就应该申请分配多少块内存空间,也就是说要建立多少个结点。当然用结构数组也可以完成上述工作,但如果预先不能准确把握学生人数,也就无法确定数组大小。而且当学生留级、退学之后也不能把该元素占用的空间从数组中释放出来。

用动态存储的方法可以很好地解决这些问题。有一个学生就分配一个结点,无须预先确定学生的准确人数,某学生退学,可删去该结点,并释放该结点占用的存储空间。从而节约了宝贵的内存资源。另一方面,用数组的方法必须占用一块连续的内存区域。而使用动态分配时,每个结点之间可以是不连续的(结点内是连续的)。结点之间的联系可以用指针实现。 即在结点结构中定义一个成员项用来存放下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。

可在第一个结点的指针域内存入第二个结点的首地址,在第二个结点的指针域内又存放第三个结点的首地址,如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。这样一种连接方式,在数据结构中称为“链表”。

2.建立动态链表及输出(主要针对单向链表)

建立单向链表的主要步骤如下:

(1)读取数据。

(2)生成新结点。 

(3)将数据存入结点的成员变量中。

(4)将新结点插入到链表中,重复上述操作直至输入结束。

在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

在C语言的头文件<stdlib.h>中,提供的内存管理函数有:

(1)分配内存空间函数malloc

调用形式:

   (类型说明符*) malloc (size);

功能:在内存的动态存储区中分配一块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。“类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。“size”是一个无符号数。

例如:

          pc=(char *)malloc(100);

表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc。

(2)释放内存空间函数free

调用形式:

   free(void*ptr);

功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc函数所分配的区域。

【例7.7】建立和输出单向动态链表。

编写函数creatlist(),建立带有头结点的单向链表。节点数据域中的数值从键盘输入,以-1作为输入结束标志。链表的头节点的地址由函数值返回。

编写函数print(),利用工具指针p从头到尾依次指向链表的每个结点,当指向某个结点时,就输出该结点的内容,直至遇到链表结束标志为止。

#include <stdio.h>

#include <stdlib.h>

struct node

{

int data;

struct node * next;

};

struct node *creatlist()

{

int i;

struct node * begin, * end, * current;

begin=(struct node *)malloc (sizeof(struct node));

end=begin;

scanf("%d",&i);

while(i!=-1)

{

current=(struct node *)malloc (sizeof(struct node));

current->data=i;

end->next=current;

end=current;

scanf("%d",&i);

}

end->next='\0';

return begin;

}

void print(struct node *head)

{

struct node *p;

p=head->next;

if(p=='\0')

printf("Linklist is null\n");

else

{

printf("head");

do

{

printf("->%d",p->data);

p=p->next;

}while(p!='\0');

}

printf("->end\n");

}

void main()

{

struct node *head;

head=creatlist();

print(head);

}

三、任务实施

1.声明一个结构体类型,其成员包括num(学号),(score(成绩),next(指针变量)。

2.将第1个结点的起始地址赋给头指针head,将第2 个结点的起始地址赋给第1个结点的next成员,第3个成员的起始地址赋给第2个结点的next成员,以此累推,将最后一个结点的next成员赋予NULL,形成链表。

3.从头指针开始,设一个指针变量p,先指向第1个结点,输出p所指的结点,然后使p后移一个结点,再输出,直至链表的尾结点。

程序代码如下:

#include<stdio.h>

#include<stdlib.h>

#define LEN sizeof(struct Student)

struct Student

{

long num;

float score;

struct Student * next;

};

int n;

struct Student * creat()

{

struct Student *head;

struct Student *p1,*p2;

n=0;

p1=p2=(struct Student *)malloc(LEN);

scanf("%ld,%f",&p1->num,&p1->score);

head=NULL;

while(p1->num!=0)

{

n++;

if(n==1)head=p1;

else p2->next=p1;

p2=p1;

p1=(struct Student *)malloc(LEN);

scanf("%ld,%f",&p1->num,&p1->score);

}

p2->next=NULL;

return(head);

}

void print(struct Student *head)

{

struct Student *p;

printf("\nNow,These %d records are:\n",n);

p=head;

if(head!=NULL)

do

{

printf("%ld %5.1f\n",p->num,p->score);

p=p->next;

}while(p!=NULL);

}

void main()

{

struct Student * head;

head=creat();

print(head);

}

程序的运行结果如下:

1001,86

1002,67

1003,98

0,0

Now,These 3 records are:

1001 86.0

1002 67.0

1003 98.0

任务五:利用共用体处理学生和教师信息

知识要点:共用体

一、任务分析

同一表格处理学生和教师信息。

学生的信息包括:姓名、号码、性别、职业、班级;

教师的信息包括:姓名、号码、性别、职业、职务。

可以看出,学生和教师的信息项目大多数是相同的,但有一项是不同的,要求把它们放在同一表格中,显然可以采用共用体来处理不同项,即将班级和职务放在同一段存储单元中。

二、必备知识与理论

共用体看起来很像结构体,只不过关键字由struct变成了union。

1共用体的概念

共用体也称为联合体,使几个不同的变量共占同一段内存的结构称为 “共用体”类型的结构。因此,共用体在同一时刻只能有一个值,它属于某一个数据成员,由于所有成员位于同一块内存,共用体的大小就等于最大成员的大小。

定义共用体类型变量:

union 共用体名

{    

成员表列

    }变量表列;

  例如:

union data 

{

int i;

char ch;

float f;

}a,b,c;

   结构体变量所占内存长度是各成员占的内存长度之和。每个成员分别占有其自己的内存单元。而共用体变量所占的内存长度等于最长的成员的长度。例如,上面定义的“共用体”变量a,b,c各占4个字节(因为一个float型变量占4个字节),而不是4+1+4=9个字节。

2.共用体变量的引用

只有先定义了共用体变量才能引用它,而且不能引用共用体变量,而只能引用共用体变量中的成员。例如,上面定义了a,b,c为共用体变量,下面的引用方式是正确的。

a.i      (引用共用体变量中的整型变量i)

a.ch (引用共用体变量中的字符变量ch)

a.f (引用共用体变量中的实型变量f)

不能只引用共用体变量,例如下面是错误的:

printf(“%f”,a);

因为a的存储区可以按不同的类型存放数据,有不同的长度,仅写共用体变量名a,系统无法知道究竟应输出哪一个成员的值。

【例7.8】引用共用体变量。

程序代码如下:

#include <stdio.h>

union Demo

{

char a;

int b;

int c;

};

main()

{

union Demo d;

d.a = 'H';

d.b = 63;

d.c = 97;

printf("size: %d\n", sizeof(d));

printf("%c\t%d\t%d\n", d.a, d.b, d.c);

}

运行结果如下:

size:4

a   97   97

说明:

共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量存储单元中值就取代。例7.7中,d.a,d.b,d.c先后被赋值,那么最终起作用的是最后被赋值的d.c=97,原来的‘H’和63都被覆盖了,对d.a按“%c”进行输出,40是字符’a’的ASCⅡ码,因此输出字符’a’。

3.共用体类型数据的特点

(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬时只能存放其中一种,而不是同时存放几种。

(2)共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。 

(3)共用体变量的地址和它的各成员的地址都是同一地址。

(4)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值,不能在定义共用体变量时对它初始化。

(5)不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针。

(6)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。

三、任务实施

先录入前4项数据,然后用if语句检查刚才录入的职业(job),如果是‘S’,表示是学生,则第5项应输入一个班级号,用输入格式符%d把一个整数送到共用体数据元素中的成同category.clas中。如果职业是‘T’,表示是教师,则输入第5项时就用输入格式符%S把一个字符串(职位)送到共用体数组元素中的成员category.position中。处理后,结构体数组元素person[0]中的共用体成员category的存储空间中,存放的是整数,而person[1]中的共用体成员category的存储空间中,存放的是字符串。

程序代码如下:

#include <stdio.h>

struct

{

int num;

char name[10];

char sex;

char job;

union

{

int clas;

char position[10];

}category;

 }person[2];

void main()

{  int i;

   for(i=0;i<2;i++)

   {

   printf("请录入人员信息:\n");

   scanf("%d %s %c %c",&person[i].num,&person[i].name,&person[i].sex, &person[i].job);

   if(person[i].job == 'S')

   scanf("%d", &person[i].category.clas);

   else if(person[i].job == 'T')

   scanf("%s", person[i].category.position);

   else

   printf("Input error!");

   }

   printf("\n");

   printf("No.   name      sex job class/position\n");

   for(i=0;i<2;i++)

   {

   if (person[i].job == 'S')

   printf("%-6d%--10s%-5c%-5c%-6d\n",person[i].num,person[i].name,

 person[i].sex, person[i].job, person[i].category.clas);

        else

       printf("%-6d%-10s%-5c%-5c%-6s\n",person[i].num,person[i].name,

person[i].sex, person[i].job, person[i].category.position);

   }

}

运行结果如下:

请录入人员信息:

001 Liyi M S 301

请录入人员信息:

002 Zhang W T prof

No.    Name sex job  class/position

1 Liyi M S 301

2 Zhang W T prof

注意:

代码中班级成员为clas(由于class是C++的关键字)。

任务六:利用共用体处理学生和教师信息

知识要点:枚举类型

一、任务分析

利用枚举类型模拟机器人控制系统中的指令,并控制机器人在平面内的移动。

机器人在移动过程中,可以接受上、下、前、后、左、右指令,利用枚举类型enum Direction{up,down,forward,back,left,right}定义机器人可以处理的指令集合。函数int move(enum Direction command,int *px,int *py)描述了翻译与执行系统,从而实现机器人根据控制指令进行移动。

二、必备知识与理论

在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。为此,C语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。

1枚举类型的定义

枚举的定义枚举类型定义的一般形式为: 

    enum 枚举名{ 枚举值表 };

在枚举值表中应罗列出所有可用值,这些值也称为枚举元素。

例如:enum weekday{sun,mon,tue,wed,thu,fri,sat};

该枚举名为weekday,枚举值共有7个,即一周中的七天。凡被说明为weekday类型变量的取值只能是七天中的某一天。

2.枚举变量的说明

如同结构和联合一样,枚举变量也可用不同的方式说明,即先定义后说明,同时定义说明或直接说明。设有变量a,b,c被说明为上述的weekday,可采用下述任一种方式:

enum weekday{ sun,mon,tue,wed,thu,fri,sat };

enum weekday a,b,c;

或者为:enum weekday{ sun,mon,tue,wed,thu,fri,sat }a,b,c;

或者为:enum { sun,mon,tue,wed,thu,fri,sat }a,b,c;

3.枚举类型的引用

枚举类型在使用中有以下规定:

枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。

例如对枚举weekday的元素再作以下赋值:

sun=5;

mon=2;

sun=mon;

都是错误的。

枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1,…,sat值为6。

【例7.9】枚举元素的序号数值。

main()

{  enum weekday

    { sun,mon,tue,wed,thu,fri,sat } a,b,c;

    a=sun;

    b=mon;

    c=tue;

    printf("%d,%d,%d",a,b,c);

}

输出结果为:0,1,2

说明:只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如:

    a=sum;

b=mon;

是正确的。而:

    a=0;

b=1;

是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换。

如:a=(enum weekday)2;

其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于:a=tue;

还应该说明的是枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。

也可以人为地指定枚举元素的数值,在定义枚举类型时显式地指定,例如:

enum weekday{ sun=7,mon=1,tue,wed,thu,fri,sat }workday,week_end;

指定枚举常量sun的值为7,mon的值为1,以后顺序加1,sat为6。

【例7.10】循环显示枚举类型。

#include <stdio.h>

void main()

{  enum body{a,b,c,d } month[31];

    int i,j;

    j=a;

    for(i=1;i<=30;i++){

      month[i]=j;

      j++;

      if (j>d) j=a;

    }

    for(i=1;i<=30;i++){

      switch(month[i])

      { case a:printf(" %2d  %c\t",i,'a'); break;

        case b:printf(" %2d  %c\t",i,'b'); break;

        case c:printf(" %2d  %c\t",i,'c'); break;

        case d:printf(" %2d  %c\t",i,'d'); break;

        default:break;

      }

    }

    printf("\n");

}

运行结果如下:

1 a 2 b 3 c 4 d 5 a 6 b 7 c 8 d 9 a 10 b

11 c 12 d 13 a 14 b 15 c 16 d 17 a 18 b 19 c 20 d

21 a 22 b 23 c 24 d 25 a 26 b 27 c 28 d 29 a 30 b

4.类型定义符typedef

C语言不仅提供了丰富的数据类型,而且还允许由用户自己定义类型说明符,也就是说允许由用户为数据类型取“别名”。类型定义符typedef即可用来完成此功能。例如,有整型量a,b,其说明如下:int a,b;

其中int是整型变量的类型说明符。int的完整写法为integer,为了增加程序的可读性,可把整型说明符用typedef定义为:

typedef int INTEGER

这以后就可用INTEGER来代替int作整型变量的类型说明了。

例如:INTEGER a,b;

它等效于:int a,b;

用typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单而且使意义更为明确,因而增强了可读性。

例如:typedef char NAME[20];    表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量,如:NAME a1,a2,s1,s2;

完全等效于:char a1[20],a2[20],s1[20],s2[20]

又如:typedef struct stu

    { char name[20];

      int age;

      char sex;

     }STU;

定义STU表示stu的结构类型,然后可用STU来说明结构变量:

STU body1,body2;

typedef定义的一般形式为:

typedef 原类型名  新类型名

其中原类型名中含有定义部分,新类型名一般用大写表示,以便于区别。

三、任务实施

机器人在移动过程中,可以接受上、下、前、后、左、右指令,利用枚举类型enum Direction{up,down,forward,back,left,right}定义机器人可以处理的指令集合。函数int move(enum Direction command,int *px,int *py)描述了翻译与执行系统,从而实现机器人根据控制指令进行移动。

程序代码如下:

#include <stdio.h>

enum Direction{up,down,forward,back,left,right};

void main()

{ enum Direction commands[10]={forward,right,forward,right,forward, right, forward, right,forward,right};

int move(enum Direction command,int *px,int *py);

int x=0,y=0;

int i=0;

for (i=0;i<10;i++)

{ move(commands[i],&x,&y);

printf("\nPosition[%d] is (%d,%d)",i+1,x,y);

}

printf("\n");

}

int move(enum Direction command,int *px,int *py)

{

int nRet=1;

static int x=0,y=0 ;

switch(command)

{

case left:

x-=1; break;

case right:

x+=1; break;

case forward:

y+=1; break;

case back:

y-=1; break;

default:

nRet=0; break;

}

*px=x;

*py=y;

return nRet;

}

运行结果如下:

Position[1] is (0,1)

Position[2] is (1,1)

Position[3] is (1,2)

Position[4] is (2,2)

Position[5] is (2,3)

Position[6] is (3,3)

Position[7] is (3,4)

Position[8] is (4,4)

Position[9] is (4,5)

Position[10] is (5,5)


http://www.kler.cn/a/534733.html

相关文章:

  • docker安装es及分词器ik
  • 分析用户请求K8S里ingress-nginx提供的ingress流量路径
  • 编程之路:在细节中磨砺技艺
  • “AI智能分析综合管理系统:企业管理的智慧中枢
  • open-webui启动报错:OSError: [WinError 1314] 客户端没有所需的特权。
  • 从Transformer到世界模型:AGI核心架构演进
  • c语言对应汇编写法(以中微单片机举例)
  • java时间相关类
  • 微信小程序~电器维修系统小程序
  • 【redis】数据类型之list
  • 深入解析色度二次采样 —— 4:4:4、4:2:2 和 4:2:0 的技术分析
  • API接口开发分享一些在实际开发中获取京东商品价格信息的方法
  • 【LeetCode】day15 142.环形链表II
  • 微服务知识——微服务拆分规范
  • 全能型免费内网穿透工具,全面支持macOS、Windows、Linux及Docker系统
  • 深入了解 MySQL:从基础到高级特性
  • 【实用技能】如何使用 DHTMLX JavaScript 组件加速初创企业发展?
  • 获取阿里云nacos注册接口状态
  • OpenHarmony的ArkTS如何进行JSON字符串的解析工作
  • 02-合并两个有序数组
  • 央行发布《贸易金融分布式账本技术要求》,参考架构包括5部分
  • Redis命令:列表模糊删除详解
  • Linux/C高级(精讲)----shell结构语句、shell数组
  • element-plus+vue3前端如何根据name进行搜索查到符合条件的数据
  • async-http-client使用示例
  • Linux网络 | 理解NATPT, 数据链路层Done