C++ 炼气期之结构体
作者:互联网
1. 前言
随着计算机
向着不同领域的延伸,数据
的概念已经不仅局限于数值型数据,计算机需要处理大量的非数值、且复杂的类型数据。
为了能抽象地描述这些非数值、复杂类型的数据,C++
引入了复合数据类型
的概念。
C++
数据类型分基本(原生)数据类型
和复合数据类型
,结构体
就是一种复合数据类型。可认为复合数据类型
是通过组合基本数据类型得到的一种新类型
,新类型
用来描述问题域中的特定数据
。
本文所用到的成员
一词指的是组成复合数据类型中的某一个子类型。
2. 结构体
现有一个开发学生管理系统
的需求,系统需要一个学生信息
管理模块,包含添加
、删除
、更新……
学生信息功能。解决这个问题之前,则需要考虑如何存储学生的个人信息以及一个学校的所有学生信息。
学生的个人信息包含学生的年龄
、性别
、成绩……
如果仅存储一个学生信息,这个问题很好解决,定义 3
个变量即可。
如果需要存储全校学生信息,可以考虑使用数组
,因受限于数组只能存储同类型数据的特点。为了完成这个需求,则需要 3
个数组,一个用来存储年龄
、一个用来存储性别
一个用来存储成绩
。显然,在编码时,需要随时随地同步 3
个数组,稍有不慎,便会出现错误。
此时,可能会有一个想法,能不能创建一个学生类型
,然后存储在数组中,数组中不再存储基本数据类型,而是一种新的学生类型
,如同二维数组一样,一维数组中存储一维数组,且不是一件很开心的事情 。
于是诞生出了一种设计理念:复合基本类型,设计出一种新的数据类型。
复合的方式有很多种,结构体
仅是其中之一。
2.1 结构体语法
//学生结构体:复合了 3 种基本数据类型
struct Student{
//学生年龄
int age;
//学生性别
char sex;
//学生成绩
float score;
};
结构体
是一种数据类型,使用语法和基本类型一样。
数据类型名 变量名;
一旦在语法上承认了这种数据类型
,和其它类型的区别就在于编译器为之所分配的内存大小。
结构体
和数组
类似。创建数组
和结构体
时,都是开辟了一个连续区域, 这个连续区域是多个变量的集合。数组
这个连续区域只能保存类型相同的数据,结构体
这个连续区域则可以存储不同类型的数据。
也就是说,在定义结构体之后,C++
运行时系统为之分配的是一个连续区域。那么这个区域有多大?是不是由组成此结构体的子数据类型的大小之和?
下面来求证一下。
首先使用c++
的sizeof
函数计算一下结构体的大小:
int main(int argc, char** argv) {
//创建结构体类型变量
Student stu;
//计算结构体的大小
int size= sizeof(stu);
cout<<size<<endl;
return 0;
}
输出结果:12
。也就是说在使用此结构体时,运行时系统分配的空间是12
。
Student
结构体由一个int
、一个char
、一个float
复合而成。理论上所需要的存储空间大小应该是4+1+4=9
。
int
是 4 字节
char
是1
字节
float
是4
字节
通过对比,可以推翻前面的结论:运行时系统为结构体
所分配的内存空间大小
并不一定是构建这个结构体的所有子数据类型的大小之和。
原因何在?
这是因为内存对齐
的缘故,内存对齐并不是本文的主题。这里只粗略说一下,运行时为结构体分配内存时,并不是我们所想象地简单地按顺序依次分配,实际上是为了提高内存的访问速度,以首地址为起始点,后续的内存空间地址尽可能是首地址的倍数。
如上图所示,在为char
类型的变量分配空间时,为了保证访问float
时的地址能被 4
整除,会为 char
类型变量填充 3
个空字节,导致结构体最后被分配到的空间是 12
。
如下结构体类型:
struct Student {
double age;
char sex;
double score;
};
在内存中占用 24
个字节,原由和上述是一样的。
对结构体有了一个大致了解后,再看一下使用结构体的 2
种语法结构:
- 静态声明。
- 动态声明。
2
种语法结构的区别在于数据存储的位置有差异性。当然,从字面意思而言,动态创建更有灵活性,事实也是如此。
2.2 静态声明
静态声明的特点:数据存储在栈中,变量中保存的是结构体本身。
如下代码:
#include <iostream>
using namespace std;
//学生结构体
struct Student {
//年龄
double age;
//性别
char sex;
//成绩
double score;
};
int main(int argc, char** argv) {
//静态声明
Student stu;
return 0;