C筑基——结构体
结构体
UDT(用户自定义类型):分为结构体、共用体和枚举类型等;
结构体类型
声明一个结构体类型的一般形式如下,使用时必须对各成员都进行类型声明即类型名 成员名;
1 | struct 结构体类型名{ //类型名用于作结构体类型的标志 |
在C++中,结构体的成员既可以包括数据也可以包括函数;
当我们对结构体类型作出定义时,相当于定义了一个模型,系统并不对之分配实际内存单元;
在使用时要定义结构体类型的变量,并在其中存放具体的数据;
[!NOTE]
结构体变量必须先定义、赋值,然后使用。
定义方法:
先声明类型再定义(推荐)
便于修改和使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//#include<string>,如果换为string类型,要引入这个头文件
using namespace std;//C++会将标准库中提供的类放到这个std文件夹里
struct Student{
int num; //占4字节
char name[20];//char数组类型,占20字节
char sex; //占1字节
int age; //按照数据成员对齐原则,在这之前要填充3字节,从28开始存,占4字节
float score; //占4字节
char addr[30];//char数组类型,如果换为string类型:string addr;
/*根据结构体整体对齐原则,在addr之后要填充2字节,使整体大小为4的整数倍(数组不看)*/
};
Student student1, student2; //定义变量,定义后系统才会分配内存单元
student1.num = 1001;
/*这里的student1.name是数组首地址,为常量指针(不可修改)*/
strcpy(student1.name,"Zhang Liang");//用 strcpy 函数把字符串的每个字符逐个复制到数组里
student1.sex = 'M';
student1.age = 19;
student1.score = 90;
strcpy(student1.addr,"Hainan");//改为string类型后可直接赋值,student1.addr = "Hainan"【号外:内存对齐原理】
数据成员对齐:每个数据成员的起始地址必须是它自身大小的整数倍;
结构体整体对齐:结构体的总大小必须是结构体中最大数据成员大小的整数倍。
若调换上例中结构体中的成员顺序,结构体整体大小也会变化,实际上,在分配存储单元时,以字为单位进行分配(4B),因此如果通过调整顺序减少了内存空间(提高内存利用率),整体所占大小也是64,而不是原本的63。
声明类型同时定义
适用于程序简单,结构体只用于本文件
1
2
3
4
5
6
7
8struct Student //声明结构体类型Student
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1, student2; //定义两个结构体类型变量,这里放着变量名表,后续可补充直接定义结构体类型变量
结构体类型仅限于本语句情况;后续无法再用这个结构体类型创建新变量;
1
2
3
4
5
6
7
8struct //无类型名,匿名类型
{ int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
} student1, student2; //只能在这定义,后续不能复用
结构体中的成员也可以是一个结构体变量;
1 | struct Date //声明一个结构体类型Date |
使用时可以将一个结构体变量的值赋给另一个具有相同结构的结构体变量;
对结构体数据不能整体性输出,必须一个一个成员地输出(输入也是如此)
结构体数组:每个数组元素都是一个结构体的数组。
1 |
|
结构体变量的地址就是该变量所占据的内存段的起始地址,可以用一个指针变量来指向一个结构体变量,如Student *p= &stu;
此时对于结构体成员的操作可分为:
- 结构体变量.成员名
- (*p).成员名 【成员运算符“ . ” 优先于“ * ”运算符,所以必须带括号】
- p->成员名
区分运算:
- p->n++ :先得到p指向的成员n的值,使用完后使值加1
- ++p->n : 先得到p指向成员n的值,加1后使用
结构体链表
用结构体变量和指向结构体变量的指针构成链表(包含两种成员:一种是用户需要用的实际数据,另一种是用来存放下一结点地址的指针变量)
链表有一个“头指针”变量以head表示,用于存放链表中第一个元素的地址;
链表中的每一个元素称为“结点”,每个结点对应两个部分,即数据加下一个结点地址;
最后一个结点无指向,因此称为“表尾”,其地址部分存放一个“NULL”空地址,表示链表到此结束;
链表中各元素在内存的存储单元中是可以不连续的
1 | /*----这里是一个静态链表的示例(所有结点都在程序定义而不是运行时临时开辟)--------*/ |
程序设计者不必具体知道各结点的具体地址,只要保证能将下一个结点的地址放到前一结点的成员next中即可。
结构体类型数据作为函数参数
(1) void print(Student stu) //用结构体变量作函数参数 【直观,但要单独为形参开辟内存单元,效率不高】
(2) void print(Student *p) //用结构体类型的指针变量作函数参数 【只将stu的起始地址传给形参】
(3) void print(Student &stud) //用结构体变量的引用作函数参数 【实参和形参代表同一对象,传递stu地址】、
动态分配/撤销内存空间
C语言利用库函数malloc和free来分配和撤销内存空间;
C++提供了较简便而功能较强的运算符new和delete。
[!WARNING]
撤销由指针变量指向的动态内存空间后,不会撤销指针变量本身。指针变量依然存在,值也不变,只是指向的值未知了
【例】
1 | int main( ) { |
【动态建立链表】
1 | do { |
共用体类型
允许在同一个存储空间中存放多个不同类型的变量
声明共用体类型:union 共用体类型名 {成员列表};【”成员列表”是用基本类型声明的多个变量】
【相同类型】
1 | union Data { //声明共用体类型 |
【不同类型】
不过共用体里只能放基本数据类型,因此string这种就不可以;
共用体的大小为最大类型的大小。
1 | union Data { //声明共用体类型 |
对于匿名共用体,一般用于直接定义共用体变量,只在本文件中使用;
在struct结构体中使用union共用体时,将union共用体作为结构体中的一个成员(此时可省略类型名);
当union声明了成员变量名时,引用union的成员需要逐级引用
枚举类型
指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内,用 enum 开头。
1 |
|
枚举元素按常量处理,枚举元素作为常量,它们是有值的,其值是一个整数
枚举值可以用来做判断比较,按整数比较规则进行比较(默认第1个枚举元素的值为0)
不能把一个整数直接赋给一个枚举变量,枚举变量只能接受枚举类型数据。
用typedef声明新的类型名
用typedef声明一个新的类型名来代替已有的类型名也可以对结构体去声明一个新的名字;
习惯上常把用typedef声明的类型名用大写字母表示
不能用来定义变量【如 typedef int a; //试图用typedef定义变量a, 非法】
步骤:
(1)按定义变量的方法写出定义语句;
(2)将变量名换成新类型名
(3)在最前面加typedef
(4)可以用新类型名去定义变量
1 | //若在一个程序中i,j是用来专门计数的 |

