目录
5、结构体指针数组在堆区、结构体在堆区、结构指针成员在堆区(了解)
一、结构体概述及定义
1、概念
一种或多种基本类型或构造类型(数组)都叫数据结构,
结构体是一种或多种基本类型或构造类型(数组)的数据的集合,即数据结构的集合。
2、定义方式
结构体定义由关键字 struct 和结构体名组成
方式1:先定义结构体类型,再定义结构体变量
struct stu
{
int num;
char name[10];
};//定义结构体stu
struct stu lucy;//定义结构体变量lucy
方式2:定义结构体类型的同时定义结构体变量
struct stu
{
int num;
char name[10];
}lucy;//定义结构体stu同时定义结构体变量lucy
方式3:定义一次性结构体
struct
{
int num;
char name[10];
}lucy;
注:结构体变量可以与成员名相同。
访问成员方式:lucy.num(继承类型,int型) lucy.name(char型,数组名代表首元素地址)
定义结构体类型及其成员时系统不分配空间,并且定义类型一般不能给成员变量赋值。
二、结构体变量的初始化
1、一般初始化
struct stu
{
int num;
char name[10];
};
void test()
{
//结构体变量的初始化,必须遵循成员的顺序以及类型
struct stu lucy={100,"lucy"};
printf("%d %s",lucy.num,lucy.name);
}
2、清空结构体变量:使用memset
3、键盘输入给结构体变量中成员赋值
4、单独操作结构体中的成员
5、相同类型的结构体变量之间整体赋值
三种方式:
三、结构体嵌套结构体
定义:
struct stu
{
int num;
char name[10];
struct data data;//嵌套结构体变量data
};
struct data
{
int year;
int month;
int day;
};
初始化及输出:
struct stu lucy = { 111,"lucy",{2002,3,7} };
printf("%d %s\n", lucy.num, lucy.name);
printf("%d %d %d", lucy.data.year, lucy.data.month, lucy.data.day);
四、结构体数组
结构体数组:本质是数组 每个元素是结构体
1、初始化赋值
2、 键盘输入给结构体赋值
五、结构体指针变量
1、 结构体指针变量
本质是指针变量 保存的是结构体变量的地址。
2、结构体数组元素的指针变量
六、结构体的指针成员
1、指针成员
指针变量作为结构体中的成员
2、结构体的指针成员指向堆区
3、浅拷贝问题
相同类型的结构体变量整体赋值时,如果结构体中没有指针成员赋值不会出现浅拷贝。 如果结构体中有指针成员 赋值 容易造成浅拷贝。
浅拷贝出现的问题:相同类型的结构体变量赋值导致各个结构体变量的指针成员指向同一处堆区空间,而各个结构体独立释放指针成员指向的空间时,造成同一处堆区空间被释放多次
struct stu bob;
bob = lucy;//浅拷贝
解决方法:如果结构体中有指针成员 尽量使用深拷贝。
struct stu bob;
bob.name = (char *)calloc(1, 128);
bob.num = lucy.num;
strcpy(bob.name, lucy.name);
4、结构体在堆区 结构体的指针成员指向堆区。(了解)
5、结构体指针数组在堆区、结构体在堆区、结构指针成员在堆区(了解)
#include <stdio.h>
#include<stdlib.h>
struct stu
{
int num;
char *name;//指针成员
};
void test()
{
struct stu **arr=NULL;
arr=(struct stu **)calloc(5,sizeof(struct stu *));///给结构体指针数组申请堆区空间
int i = 0;
for ( i = 0; i < 5; i++)
{
//给指针数组中的每个元素 申请结构体堆区空间
arr[i] = (struct stu*)calloc(1, sizeof(struct stu));
//每个结构体中的name成员申请堆区空间
arr[i]->name = (char*)calloc(1, 100);
arr[i]->num = 10 + i;
sprintf(arr[i]->name, "姓名%d", i + 1);
}
for ( i = 0; i < 5; i++)
{
printf("%d %s\n", arr[i]->num, arr[i]->name);
}
//释放
for ( i = 0; i < 5; i++)
{
//先释放结构体中的name的指向
if (arr[i]->name != NULL)
{
free(arr[i]->name);
arr[i]->name = NULL;
}
//释放结构体
if (arr[i] != NULL)
{
free(arr[i]);
arr[i] = NULL;
}
//释放整个arr指针数组
if (arr != NULL)
{
free(arr[i]);
arr= NULL;
}
}
}
七、结构体的内存分配对齐规则
1、自动对齐规则
(1)确定分配单位由 结构体中最大的基本类型 长度决定。
(2)确定成员的偏移量
成员偏移量 = 成员自身类型长度的整数倍。倍数由从前往后未被占用的内存空间决定。
(3)结构体的总大小 = 分配单位整数倍
经典案例:画出以下结构体的对齐
struct Data
{
char a;//成员偏移量 =1B*0
short b;//成员偏移量 =2B*1
int c;//成员偏移量 =4B*1
char d;//成员偏移量 =1B*8
short e;//成员偏移量 =2B*5
};
最大基本类型为int,分配单位4B,内存分配对齐:
a | b | b | |
c | c | c | c |
d | e | e |
故总共12B
2、结构体嵌套结构体 自动对齐规则
(1)确定分配单位由 全部结构体中最大的基本类型 长度决定。
(2)确定成员的偏移量
普通成员偏移量 = 成员自身类型长度的整数倍。倍
被嵌套的结构体整体偏移量 = 该结构体中最大的基本类型整数倍
以上倍数由从前往后未被占用的内存空间决定。
(3)结构体的总大小 = 分配单位整数倍
嵌套结构体大小 = 该结构体中最大的基本类型整数倍
内存分配情况:共16B
a2 | |||
a1 | |||
b1 | b1 | b1 | b1 |
c2 | c2 |
3、强制对齐
#pragma pack (value)时的指定对齐值value(value值为)
(1)确定分配单位 = min(结构体中最大的基本类型, value)
(2)确定成员的偏移量 成员偏移量 = 成员自身类型的整数倍。
(3)收尾工作 结构体的总大小 = 分配单位整数倍
八、结构体的位域
1、位域的概念
在结构体中,以位为单位的成员,咱们称之为位段(位域)
总共占32位4B
没有非位域隔开且类型相同的位域 叫相邻位域。
相邻位域可以压缩。但是压缩的位数不能超过自身类型的大小。
不能对位域取地址(因:系统分配地址以字节为单位)
2、防止位溢出
对位域赋值不能超过位域本身位的宽度
3、另起一个存储单元
4、无意义位段
九、共用体union
结构体:所有成员拥有独立空间
共用体:所有成员共享同一块空间
例:
union Data
{
char a;
short b;
int c;
};
成员a b c共享同一块空间,空间大小由最大的成员空间决定,data为4B。
体现共享:
成员a b c共享同一块空间,但是每个成员 能操作的空间的范围 是由成员自身类型长度决定
十、枚举
枚举:将枚举变量 要赋的值 一一列举出来
以扑克花色为例枚举四种花色:
enum POKER_COLOR{HONGTAO,MEIHUA,FANGKUAI,HEITAO};
若修改某个枚举列表的值,从修改处再依次递增。