结构体的定义及应用

 在实际应用中会遇到某类信息必须用不同数据类型的数据项来描述的情况,如学生成绩信息中含有:
  学号 ---int
  姓名 ---char[8] 
  性别 ---char
  成绩 ---float
  等数据项。这些数据项属于不同的数据类型,如学号为整型,性别为字符型等。将不同类型数据作为一个整体来处理的数据结构称为结构体。
  结构体类型是一种导出的数据类型,编译程序并不为任何数据类型分配存储空间,只有定义了结构体类型的变量后,系统才为这种变量分配存储空间。如:对整数类型 int ,系统并不为其分配存储空间,只有定义整型变量 int a后,系统才为变量a分配存储空间。与枚举类型相似,必须先定义结构体类型,然后才能用结构体类型定义结构体变量。

  结构体类型的定义

  定义结构体类型的语句格式为:
  struct <结构体类型名>
  { <类型> <成员1>;
  <类型> <成员2>;
  …
  <类型> <成员n>;
  };
  关键词struct说明,定义的是结构体类型,结构体类型名由标识符组成。由定义格式可以看出,结构体数据类型由若干个数据成员组成,每个数据成员可以有不同的数据类型。数据类型可以是基本类型,也可以是导出类型。

  【例】定义一个学生成绩的结构体数据类型如下:
 struct student 
 { int no ; //学号
  char name[8]; //姓名
  float eng,phy,math,ave ; //英语、物理、数学与平均成绩 
 };
  学生成绩结构体中的数据成员有no(学号)、name[8](姓名)、eng(英语)、phy(物理)、math(数学)、平均成绩(ave)。每个成员的类型可以是基本类型或导出类型。在一个程序中,一旦定义了一个结构体类型,就增加了一种新的称为结构体的数据类型,也就可以用这种数据类型定义结构体变量。

 结构体变量的定义

  定义结构体变量有三种方式,即:先定义结构体后定义结构体变量,定义结构体的同时定义结构体变量,在函数内直接定义结构体变量。现介绍如下:
  1.先定义类型后定义变量
  格式: 〔存储类型〕<结构体类型名> <变量名1> 〔,<变量名2>,…,<变量名n>〕;
  用上例中的结构体类型student 可定义变量与数组如下: 
  student stu1,stu2[10];
  此时,系统为stu1分配了28个字节存储空间,系统为数组stu2 分配10个与stu1相同的存储空间。


  2.定义结构体的同时定义结构体变量
  格式:struct <结构体类型名>
  { <成员列表>} <变量名1>〔,<变量名2>…〕;
  

  3.直接定义结构体变量
  struct 
  { <成员列表>} <变量名1>〔,<变量名2>…〕;
  现就三种定义方式举例说明如下。
  【例8.4】用三种方式定义职工、学生、日期三类结构体变量。
 # include <iostream.h>
 # include <string.h>
 struct employee //定义职工结构体数据类型 
 { int no ; //职工编号
  char name[8]; //职工姓名
  char addr[40]; //家庭地址
 };
 struct student //定义学生结构体数据类型
 { int no; //学号
  float eng,phy,math,ave; //英语、数学、物理成绩、平均成绩
 } stu1,stu2; //定义结构体同时定义结构体变量stu1、stu2
 main ()
 { employee emp1,emp2; //定义employ 类型的结构体变量emp1、emp2。
  struct 
   { int year,month,day;
   } date1,date2; // 直接定义日期结构体类型变量。
  emp1.no=100; //赋职工号
  strcpy ( emp1.name, "zhang sa"); //赋职工姓名
  strcpy( emp1.addr, "Nanging"); //赋家庭地址
  emp2=emp1; //将职工emp1的信息赋给emp2
  cout<<"no="<< emp2.no<<'\t' //输出职工信息
  <<"name="<< emp2.name<<'\t' 
  << "addr="<< emp2.addr<<endl;
  stu1.no=101; //输入学号与成绩 
  stu1.eng=90; //输入成绩
  stu1.phy=95;
  stu1.math=100;
  cout<<"eng="<<stu1.eng<<'\t' //输出学生成绩
  <<"phy="<<stu1.phy<<'\t' 
  <<"math="<<stu1.math<<endl;
  date1.year=1999; //输入入学日期 
  date1.month=09;
  date1.day=01;
  cout<<"year="<<date1.year<<'\t' //输出入学日期
  <<"month="<<date1.month<<'\t'
  <<"day="<<date1.day<<endl;
 }
  在上例中,结构体变量emp1、emp2采用先定义结构体后定义变量的方式。结构体变量stu1、stu2采用定义结构体的同时定义结构体变量。结构体变量date1、date2采用在函数内直接定义结构体变量。几点说明:
  (1)结构体变量的初始化
  与数组类似,在定义结构体变量时可进行初始化。如:
  employee emp1={100, "zhang sa","Nanging"};
  注意:在初始化时,数据类型必须保持一致。如:
  employee emp1={"100", "zhang sa", "Nanging"};
  由于 emp1.no为整型,而初始化数据"100"为字符型,所以编译出错。
  (2)通常在设计大程序时,将所有结构体类型说明存放在头文件中,然后用包含命令将该头文件嵌入程序,编程时用结构体名定义结构体变量即可。因此,提倡使用第一种方式定义结构体变量。
  (3)结构体变量的作用域
  结构体变量的作用域与普通变量的作用域相同,即在函数外定义的结构体变量具有文件作用域。在函数内定义的结构体变量具有块作用域。如:stu1、stu2是在函数外定义的结构体变量,因此,stu1、stu2为全局变量且具有文件作用域。而emp1、emp2、date1、date2是在函数内定义的结构体变量,因此,emp1、emp2 、date1、date2为局部变量且只具有块作用域。
  (4)指定结构体变量的存储类型
  定义结构体变量时可以指定其存储类型,如:
  static employee emp1; //指定emp1为静态结构体变量
  auto employee emp2; //指定emp2为自动结构体变量
  extern employee emp3; //指定emp3为外部结构体变量

  结构体变量的引用

  一旦用结构体类型定义好结构体变量后,便可引用结构体变量中的成员。引用结构体变量成员的格式为: <结构体变量>.<成员名>
  其中"."称为成员运算符。在例 8.4中已多次用到"<结构体变量>.<成员名>",如stu1.name表示引用学生结构体变量stu1中的姓名数据成员name。对结构体变量应注意以下几点:
  (1)同类型结构体变量可以直接赋值,如:emp2=emp1; 该赋值操作是将emp1中每个成员值赋给emp2对应成员。
  (2)结构体变量不能直接输入/输出,只能对其成员进行输入输出。如:
  cin >> stu2; 是错误的
  cin >> stu2.name>>stu2.addr; 是正确的
  (3)结构体变量可作为函数参数
  当结构体变量作为函数参数时,实参值传送给形参属于值传送,因此,函数调用后实参值仍保持不变。
 

  【例】定义一个复数结构体,以复数的实部r、虚部i作为结构体成员,编写能实现两复数加法的函数,用结构体变量作为其形参。在主函数内定义两个复数,并赋初值。调用复数加法函数,完成两复数相加的操作。 
  分析:复数的一般表达式为 复数=实部+虚部 i ,因此,复数结构体类型中两个数据成员为实部r与虚部i。两个复数相加是指其实部与实部相加,虚部与虚部相加,因此,复数加法函数add()中,先定义一个用于存放两复数和的临时复数变量c,然后进行两个复数实部与实部相加,虚部与虚部相加的操作,结果存放在c的实部与虚部中,最后将c作为函数返回值。编写程序如下:
 # include <iostream.h>
 struct Complex //定义一个复数结构体
 { float r; //复数的实部
  float i; //复数的虚部
 };
 Complex add(Complex c1,Complex c2) //复数加法函数
 { Complex c; //定义复数结构体变量c
  c.r=c1.r+c2.r; //两复数实部相加赋给c的实部 
  c.i=c1.i+c2.i; //两复数虚部相加赋给c的虚部
  return c; //返回相加后的复数c
 }
 void main( void )
 { Complex x1={10,25},x2={20,35},x; //定义三个复数x1、x2、x
  x=add (x1,x2); //调用复数加法函数
  cout<<"x1="<<x1.r<<"+"<<x1.i<<"i"<<endl;
  cout<<"x2="<<x2.r<<"+"<<x2.i<<"i"<<endl;
  cout<<"x="<<x.r<<"+"<<x.i<<"i"<<endl;
 }
  执行程序后输出
  x1=10+25i
  x2=20+35i
  x =30+60i
  主函数main()中定义了三个复数类型的结构体变量x1、x2、x,并对x1、x2赋初始值。调用复数加法函数add(),完成x1与x2相加的操作。实参x1、x2的值分别传给形参c1、c2,即:c1.r=x1.r=10, c1.i=x1.i=25, c2.r=x2.r=20, c2.i=x2.i=35。该参数传送属于值传送,因此程序执行后实参x1、x2的值并未改变。运算结果的复数值是通过函数返回,因此,函数返回类型应定义为复数结构体类型。

  结构体数组

  由结构体类型元素组成的数组称为结构体数组。结构体数组定义方法与结构体变量完全类似,只要在结构体变量定义的基础上加上维数说明即可。
  例如: struct student 
    { int no;
     float eng,phy,math,ave;
    } stu1[10],stu2[10][20];
  分别定义了一维数组stu1和二维数组stu2。
  【例】建立学生档案的结构体数组,描述一个学生的信息:姓名、性别、年龄、出生日期。并输出已建立的学生档案。
 #include <iostream.h>
 struct date //定义出生日期结构体类型
 { int year,month,day; //定义出生年、月、日 数据成员
 };
 struct student //定义学生档案结构体类型
 { int no ; //学号
  char name[10]; //姓名 
  char sex[2]; //性别
  int age; //年龄
  date birthday; //出生日期(结构体类型)
 };
 void output( student x) //定义输出函数
 { cout << x.no <<'\t'<<x.name<<'\t'<< x.sex <<'\t'<< x.age <<'\t'
   <<x.birthday.year<<'\t'<< x.birthday.month<<'\t'<< x.birthday.day<<endl;
 }
 void main(void)
 { cout <<"no"<<'\t'<<"name"<<'\t'<<"sex"<<'\t'<<"age"<<'\t'
  <<"year"<<'\t'<< "month"<<'\t'<<"day"<<endl;
  student s[2]={{100,"zhou","m",22,1980,12,1},{101,"Li","w",24,1978,1,1}};
  for ( int j=0 ;j<2;j++) output( s[j]);
 }
  程序执行后输出:
  no name sex age year month day
  100 zhou m 22 1980 12 1
  101 Li w 24 1978 1 1
  由上例可以看出:
  (1)定义结构体数组时,也可进行初始化赋值操作。
  (2)在结构体中可以用其它结构体类型定义数据成员。如在student结构体中用date结构体定义出生日期数据成员birthday。对于在结构体中定义的结构体数据成员,其引用方式为:结构体变量名.结构体成员名.成员名。
  如引用结构体变量x的数据成员birthday中成员yaer、month、day的方式为:
  出生年份:x.brithday.year
  出生月份:x.brithday.month
  出生日期:x.brithday.day
  

  【例】定义全班学生学习成绩的结构体数组,学生结构体类型的数据成员为:姓名、学号、英语、物理、数学和这三门功课的平均成绩(通过计算得到)。设计四个函数:全班成绩输入,求出每个学生的平均成绩,按平均成绩的升序排序,输出全班成绩表。
 # include <iostream.h>
 struct student //定义学生成绩结构体类型
 { int no;
  char name[8];
  float eng,phy,math,ave;
 };
 void Input (student s[ ],int n) //输入函数
 { int i;
  cout <<"输入学生:"<<endl;
  cout <<"学号、姓名、英语、物理、数学成绩"<<endl;
  for (i=0;i<n;i++)
  cin >> s[i].no >>s[i].name>>s[i].eng>>s[i].phy>>s[i].math;
 } 
 void Ave (student s[ ],int n) //求平均成绩函数
 { int i;
  for (i=0;i<n;i++)
  s[i].ave=(s[i].eng+s[i].phy+ s[i].math)/3;
 }
 void Sort(student s[ ],int n) //升序排序函数
 { int i,j,k;
  student temp;
  for (i=0;i<n-1;i++)
   { k=i;
   for (j=i+1;j<n;j++)
     if (s[j].ave<s[k].ave ) k=j;
    if (k>i) 
   {temp=s[k];s[k]=s[i];s[i]=temp;}
   }
  }
 void Print(student s[ ], int n) //输出函数
 { int i;
  cout<< "学号" << '\t' << "姓名" << '\t' << "英语" << '\t' << "物理" << '\t' 
  << "数学" << '\t'<< "平均成绩" << '\n';
  for (i=0;i<n;i++)
  cout<<s[i].no<<'\t' <<s[i].name<<'\t'<<s[i].eng<<'\t' <<s[i].phy<<'\t' 
  <<s[i].math<<'\t' <<s[i].ave<<'\n';
 }
 void main (void)
 { student stu[3]; //定义结构体数组
  Input(stu,3); //输入学生成绩 
  Ave (stu,3); //计算学生平均成绩
  Sort(stu,3); //按平均成绩排序 
  Print(stu,3); //输出学生成绩
 } 
  程序执行后提示:
  输入学生:
  学号、姓名、英语、物理、数学成绩:
  用户输入:
  100 Zhou 90 85 80
  101 Li 80 75 70
  102 Zhang 100 95 90
  屏幕输出
  学号 姓名 英语 物理 数学 平均成绩
  101 Li 80 75 70 75
  100 Zhou 90 85 80 85
  102 Zhang 100 95 90 95
  程序说明:
  用结构体数组作为函数参数属于传地址,即实参stu[ ]与形参s[ ]共用同一内存区。因此,在输入、输出、计算平均成绩、排序四个函数内对数组s[ ]的操作,就是对主函数中数组stu[ ]的操作。

posted @ 2013-09-23 19:58  herizai007  阅读(1519)  评论(0编辑  收藏  举报