引出结构体
有时需要将不同类型的数据组合成一个有机的整体,以供用户方便地使用。这些组合在一个整体中的数据是互相联系的。例如,一个学生的学号、姓名、性别、年龄、成绩、家庭地址等项,都是这个学生的属性。
则可以通过下面的声明来建立所示的数据类型:
struct Student{ //声明一个结构体类型Student
int num; //包括一个整型变量num
char name[20]; //包括一个字符数组name,可以容纳20个字符
char sex; //包括一个字符变量sex
int age; //包括一个整型变量age
float score; //包括一个单精度型变量
char addr[30]; //包括一个字符数组addr,可以容纳30个字符
}; //最后有一个分号
声明一个结构体类型的一般形式为:
struct 结构体类型名
{成员表列};
注意:在C语言中,结构体的成员只能是数据(如上面例子中所表示的那样)。C++对此加以扩充,结构体的成员既可以包括数据(即数据成员),又可以包括函数(即函数成员),以适应面向对象的程序设计。
定义结构体类型变量的方法
1.先声明结构体类型再定义变量。
比如:Student student1,student2;
在定义了结构体变量后,系统会为之分配内存单元。例如student1和student2在内存中各占63个字节(4+20+1+4+4+30=63)。
2.在声明类型的同时定义变量:
这种形式的定义的一般形式为:
struct 结构体名
{
成员表列
}变量名表列;
struct Student{ //声明结构体类型Student
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1,student2; //定义两个结构体类型Student的变量student1,student2
3.直接定义结构体类型变量
其一般形式为:
struct{ //注意没有结构体类型名
成员表列
}变量名表列;
结构体的成员也可以是一个结构体变量,如:
struct Date{ //声明一个结构体类型Date
int month;
int day;
int year;
};
struct Student{ //声明一个结构体类型Student
int num;
char name[20];
char sex;
int age;
Date birthday; //Date是结构体类型,birthday是Date类型的成员
char addr[30];
}student1,student2; //定义student1和student2为结构体类型Student的变量
结构体变量的初始化
和其他类型变量一样,对结构体变量可以在定义时指定初始值:
struct Student{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}student1={10001,"ZhangXin",'M',19,90.5,"Shanghai"};
也可以采取声明类型与定义变量分开的形式,在定义变量时进行初始化:
Student student1={10001,"ZhangXin",'M',19,90.5,"Shanghai"};
结构体变量的引用
(1).可以将一个结构体变量的值赋给另一个具有相同结构的结构体变量:
student1= student2;
(2).可以引用一个结构体变量中的一个成员的值。例如,student1.num表示结构体变量student1中的成员的值。
引用结构体变量中成员的一般方式为:
结构体变量名.成员名
(3).如果成员本身也是一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。例如,对上面定义的结构体变量student1,可以这样访问各成员:
student1.birthday.month;
(4).不能将一个结构体变量作为一个整体进行输入和输出,比如:
cout<<student1;
(5).对结构体变量的成员可以像普通变量一样进行各种运算:
student2.score=student1.score;
sum=student1.score+student2.score;
student1.age++;
++student1.age;
由于“.”运算符的优先级最高,因此student1.age++相当于(student1.age)++。++是student1.age进行自加运算,而不是先对age进行自加运算。
(6).可以引用结构体变量成员的地址,也可以引用结构体变量的地址:
cout<<&student1; //输出student1的首地址
cout<<&student1.age; //输出student1.age的地址
结构体变量的地址主要用作函数参数,将结构体变量的地址传递给形参。
结构体数组
一个结构体变量中可以存放一组数据(如一个学生的学号、姓名、成绩等数据)。如果有10个学生的数据需要参加运算,显然应该用数组,这就是结构体数组。结构体数组与以前介绍过的数值型数组的不同之处在于:每个数组元素都是一个结构体类型的数据,它们都分别包括各个成员项。
定义结构体数组
和定义结构体变量的方法相仿,定义结构体数组时只需声明其为数组即可。
struct Student{ //声明结构体类型Student
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
Student stu[3]; //定义Student类型的数组stu
struct Student{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3];
struct{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3];
数组各元素在内存中连续存放:
结构体数组的初始化
struct Student{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}sty[3]={\{10101,"Li Lin",'M',18,87.5,"103 Beijing Road"},
{10102,"Zhang Fun",'M',19,99,"130 Shanghai Road"},
{10104,"Wang Min",'F',20,78.5,"1010,ZhongshanRoad"}\};
定义数组stu时,也可以不指定元素个数,即写成以下形式:
stu[]={\{…},{…},{…}\};
编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。一个结构体常量应包括结 构体中全部成员的值。当然,数组的初始化也可以用以下形式:
Student stu[]={\{…},{…},{…}\}; //已事先声明了结构体类型Student
指向结构体变量的指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素。
1.通过指向结构体变量的指针引用结构体变量中的成员:
struct Student{ //声明结构体类型student
int num;
string name;
char sex;
float score;
};
Student stu; //定义Student类型的变量stu
Student *p=&stu; //定义p为指向Student类型数据的指针变量并指向stu
cout<<(*p).num<<" "<<p->num<<endl;
为了使用方便和使之直观,C++提供了指向结构体变量的运算符->,例如p->num表示指针p当前指向的结构体变量中的成员num。p->num和(*p).num是等价。
也就是说,以下3种形式等价:
1.结构体变量.成员名。如stu.num;
2.(*p).成员名。如(*p).num;
3.p->成员名。如p->num。“->”称为指向运算符。
p->n: 得到p指向的结构体变量中的成员n的值。
p->n++: 得到p指向的结构体变量中的成员n的值,用完该值后使它加1。
++p->n: 得到p指向的结构体变量中的成员n的值,并使之加1,然后再使用它。
用结构体变量和指向结构体变量的指针构成链表
链表是一种常见的重要的数据结构。如图表示最简单的一种链表(单向链表)的结构:
链表有一个“头指针”变量,图中以head表示,它存放一个地址。该地址指向一个元素。链表中的每一个元素称为“结点”,每个结点都应包括两个部分:一是用户需要用的实际数据,二是下一个结点的地址。 可以看到链表中各元素在内存中的存储单元可以是不连续的。要找某一元素,可以先找到上一个元素,根据它提供的下一元素地址找到下一个元素。
声明一个结构体类型,包含两种成员,一种是用户需要用的实际数据,另一种是用来存放下一结点地址的指针变量。例如:
struct Student{
int num;
float score;
Student *next; //next指向Student结构体变量
};
结构体类型数据作为函数参数
1.用结构体变量作函数参数:
#include <iostream>
#include <string>
using namespace std;
struct Student{ //声明结构体类型Student
int num;
char name[20];
float score[3];
};
void print(Student stu){
cout<<stu.num<<" "<<stu.name<<" "<<stu.score[0]<<" "<<
stu.score[1]<<" "<<stu.score[2]<<endl;
}
int main( ){
Studentstu; //定义结构体变量
stu.num=12345; //以下5行对结构体变量各成员赋值
stu.name="LiFung";
stu.score[0]=67.5;
stu.score[1]=89;
stu.score[2]=78.5;
print(stu); //调用print函数,输出stu各成员的值
}
用结构体变量作实参和形参,程序直观易懂,效率是不高的。因为在实参传递给形参的时候需要从实参中每一个元素拷贝给形参。
2.用指向结构体变量的指针作实参:
#include <iostream>
#include <string>
using namespace std;
struct Student{
int num;
string name; //用string类型定义字符串变量
float score[3];
}stu={12345,"LiFung",67.5,89,78.5}; //定义结构体student变量stu并赋初值
void print(Student *p){
cout<<stu->num<<" "<<stu->name<<" "<<stu->score[0]<<" "<<
stu->score[1]<<" "<<stu->score[2]<<endl;
}
int main(){
Student*pt=&stu; //定义基类型为Student的指针变量pt,并指向stu
print(pt); //实参为指向Student类数据的指针变量
}
采用指针变量作为实参和形参,空间和时间的开销都很小,效率较高。但程序(2)不如程序(1)那样直观。
3.用结构体变量的引用作函数参数:
#include <iostream>
#include <string>
using namespace std;
struct Student{
int num;
string name; //用string类型定义字符串变量
float score[3];
}stu={12345,"LiFung",67.5,89,78.5}; //定义结构体student变量stu并赋初值
void print(Student &stu){
cout<<stu.num<<" "<<stu.name<<" "<<stu.score[0]<<" "<<
stu.score[1]<<" "<<stu.score[2]<<endl;
}
int main(){
print(stu); //实参为结构体Student变量
}
程序(3)的实参是结构体Student类型变量,而形参用Student类型的引用,虚实结合时传递的是stu的 地址,因而效率较高。它兼有(1)和(2)的优点。引用变量主要用作函数参数,它可以提高效率,而且保持程序良好的可读性。