六、结构体
6.1 结构体概念
- 结构体(struct):是由一系列具有相同类型或不同类型的数据构成的数据集合。
- “结构”是一种构造类型,它是由若干“成员”组成的。
- 每一个成员可以是一个基本数据类型或者又是一个构造类型。
- 结构即是一种“构造”而成的数据类型, 那么在说明和使用之前必须先定义它,也就是构造它。
6.2 C++语言中的结构体
6.2.1 结构体的声明和定义
-
声明一个结构体类型的一般形式为:
-
struct 结构体名{ 成员列表; };
-
eg:
struct Stu{//Stu是声明的一个结构体类型名 int score;//整型成员变量 int math;//整型成员变量 char name[10];//字符型成员变量 };
-
struct为结构体关键字,上面只是声明了一个结构构造类型
Stu
-
和
int,float
类似,Stu
是一个类型名,可以定义结构体变量 -
类型声明只是告诉系统此类型的构造形式,并不会实际分配内存
-
注意:结构体的声明和定义必须以分号结束。
-
-
定义结构后,便可以创建这种类型的变量了:
Stu a;//定义一个结构体变量a Stu hat;//定义一个结构体变量hat
-
(2) 示例代码
-
此声明声明了拥有
3
个成员的结构体,分别为整型的a
,字符型的b和双精度的c
,但没有标明其标签,声明了结构体变量s1
struct { int a; char b; double c; } s1;//s1是结构体变量
-
此声明声明了拥有
3
个成员的结构体,分别为整型的a
,字符型的b
和双精度的c
,结构体的类型名被命名为Stu
,另外定义了变量t1, t2[20], *t3
struct Stu{ int a; char b; double c; }; Stu t1, t2[20], *t3;
-
可以用
typedef
创建新类型,此声明声明了拥有3
个成员的结构体,分别为整型的a
,字符型的b
和双精度的c
,结构体的标签被命名为Simple2
,用Simple2
作为类型声明新的结构体变量u1, u2[20], *u3
typedef struct Simple{//Simple是结构体类型名 int a; char b; double c; } Simple2;//注意:Simple2也是结构体类型名,相当于Simple的别名 Simple2 u1, u2[20], *u3;//若去掉typedef则编译报错,error C2371: “Simple2”: 重定义;不同的基类型
-
同类型的结构体可以直接赋值。
struct Stu{ int score; int math; char name[10]; }; Stu a,b={100,50,"Tom"}; a=b;//正确,a,b是同为Stu类型的结构题变量
-
6.3 结构体内存分配机制
- 结构体和其他的数据类型是一样的,在定义一个结构体的时候,系统并不会真正的分配内存空间,只有在定义变量的时候,才会分配。
- 结构体对于成员变量的内存分配有以下要求:
-
结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
-
结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
-
结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节
-
例如:
-
struct Stu { char a; int b; char c; };
- 在
Stu
中,最宽数据类型为int
,在32
位系统中为4Byte
); - 参照第二条,第一个数据是
char
,存储一个char
占1 Byte
,要保证下一个int
的存储起始地址是4
的整数倍,所以要在char
后面填充3 Byte
,然后再存储int
变量b
。 - 最后一个也是
char
类型,他就占1 Byte
,肯定是他存储位置的整数倍 - 最后参照第三条,结构体的总大小为最宽数据类型的整数倍,所以会在第二个
char
之后再填充3 Byte
。 - 这样的话,总共占据的空间是1+3+4+1+3 = 12(红色为填充字节)
- 在
-
但是,调换一下结构中数据成员的顺序:
struct s1 { int b; char a; char c; };
- 同样地分析4+1+1+2 = 8
- 相比之下,存储空间的效率提高33%。
- 提示:在竞赛阶段,为了节约内存,在组织数据结构的数据成员的时候,可以将相同类型的成员放在一起,这样就减少了编译器为了对齐而添加的填充字符。
-
6.4 结构体初始化
- 定义时初始化
struct Stu{ int Num; bool Sex; char Name[20]; }; //定义时初始化 Stu a={0};//初始化所有成员为0 Stu b={15,1,"Tom"};//依次为成员赋值
- 构造函数初始化
方法一: struct Stu{ int Num; bool Sex; char Name[20]; //构造函数初始化 Stu(){ Num=0; Sex=false; memset(Name,0,sizeof(Name)); } }; 方法二: struct Stu{ int Num; bool Sex; char Name[20]; //构造函数初始化 Stu(){ memset(this->Name,0,sizeof(Name)); } };
- 结构体成员的访问可以使用成员运算符:"."
struct Stu{ int Num; bool Sex; char Name[20]; //构造函数初始化 Stu(){ memset(this,0,sizeof(Name)); } }; Stu a; //访问结构体a的成员函数 a.Num=10; a.Sex=1; a.Name="Tom";
6.5 结构体重载运算符重载
6.5.1 运算符重载
- 基本概念
- 重载的运算符是具有特殊名字的函数
- 由关键字 \(operator\) 和要重载的运算符号共同组成。\(eg: operator +()\)
- 重载运算符也包含返回类型、参数列表以及函数体
- 重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。(一元运算符有一个参数,二元有两个参数)
- 如果运算符函数是成员函数,则它的第一个运算对象绑定到隐式的 \(this\) 指针上,因此显式的参数比实际参数少一个
- 只能重载已有的运算符,无权发明新的运算符
- 可以重载绝大多数运算符。(不能重载 :: , . ,.* ,?: )
- 通常情况下,不应重载逗号、取地址、逻辑与、逻辑或运算符
6.5.2 结构体重载运算符重载
-
结构体是构造类型,是无法直接进行逻辑运算、四则运算等,如果需要,我们可以对相应的运算符进行重载
-
结构体重载小于号 "<"
-
struct Stu{ int score;//总分 int math;//数学 char Id[maxn];//学号 //方式一:作为成员函数重载,二元运算符,省略第一个参数,第一个const表示形参为只读,第二个const表示 //不允许修改成员变量,固定格式。 bool operator <(const Stu &a)const{ if(score==a.score){//如果总分相等按数学进行比较 //如果总分、数学都相等按学号进行比较 if(math==a.math)return (strcmp(Id,a.Id)<0); //如果总分相等,数学不等,数学进行比较 return math>a.math; } //如果总分不等,按总分进行比较 return score>a.score; } }; //方法二:全局重载 bool operator <(const Stu &a,const Stu &b){ if(a.score==b.score){//如果总分相等按数学进行比较 //如果总分、数学都相等按学号进行比较 if(a.math==b.math)return (strcmp(a.Id,b.Id)<0); //如果总分相等,数学不等,数学进行比较 return a.math>b.math; } //如果总分不等,按总分进行比较 return a.score>b.score; }
-
结构体重载小于号 "+"
struct Stu{ int score;//总分 int math;//数学 //方式一:作为成员函数重载,二元运算符,省略第一个参数,第一个const表示形参为只读,第二个const表示 //不允许修改成员变量,固定格式。tongxue Stu operator +(const Stu &a)const{ Stu temp;//临时结构体变量 temp.score=this->score+a.score;//this->score等价与score表示改结构题的score成员变量 temp.math=this->math+a.math; rerturn temp;//计算后的结构变量 } }; //全局重在略……
-
-
结构体成员函数和构造函数
struct Stu{ int score;//总分 int math;//数学 char Id[maxn];//学号 //创建结构题时会自动创建一个默认的同名构造函数,构造函数在定义变量时会自动调用 Stu(){ memset(this,0,sizeof(Stu)); } //成员函数,需要调用才会执行 void Init(){ memset(this,0,sizeof(Stu)); } //成员函数是自己定义的实现某些功能的函数,需要调用才能使用 void Print(){ printf("%s\n",Id); } }a; //调用 a.Init(); a.Print();
-
有能力的同学可以尝试这重载一下高精度加、减、乘法。
struct Stu{ int a[maxn]; Stu(){ memset(this,0,sizeof(Stu)); } //重载赋值语句 Stu operator =(const Stu &b){ for(int i=0;i<=b.a[0];++i) a[i]=b.a[i]; return *this; } //重载赋值语句 Stu operator +(const Stu &b){ Stu c; int i=1; while(i<=a[0] || i<=b.a[0]){ c.a[i]+=a[i]+b.a[i]; c.a[i+1]=c.a[i]/10; c.a[i]%=10; ++i; } if(c.a[i])c.a[0]=i; else c.a[0]=i-1; return c; } void read(){ char s[maxn]; scanf("%s",s); a[0]=strlen(s); for(int i=0;i<a[0];++i) a[a[0]-i]=s[i]-48; } void Print(){ for(int i=a[0];i>0;--i) printf("%d",a[i]); printf("\n"); } };
hzoi