结构体类型
1 结构体类型的不完成声明
这种声明只能使用一次
了解就行
如
struct
{
char c;
int a;
}a,b;
struct
{
char c;
int a;
} *p;
&a == p;//编译器会报错,编译器会认为是两种类型,是非法的,所以最好别这样声明
最好别这样使用
2 结构体的初始化
使用大括号
要善于使用memset
typedef struct B
{
char name[20];
int age;
}b;
struct A
{
B stu;
char c;
int a;
}A;
b = {zhangsan,19};//单独初始化
A={{zhangsan,19},a,90}//结构题嵌套,那就在里面再加一个{},先初始化里面那个结构体
若里面嵌套的是一个结构体数组呢,那就不好初始化了呢
直接用前面提到的memset,如
struct A
{
B stu[20];
char c;
int a;
}C;
用memset初始化C
先看看这个函数memset如何使用吧(我们上cplusplus.com - The C++ Resources Network上面可查找函数使用)
ptr 传入要初始化数据的首地址,value要初始化的值,num初始化的大小
所以这里我们可以这样做
memset(&c, 0, sizeof(C));
这里就将sizeof(C)那么个字节大小的内存,每个内存都初始化为0;
如何求结构体类型的大小
先看看
struct P
{
char a;
int b;
double c;
}P;
P的大小是多少,先猜一猜,首先是不是觉得是1+4+8=13字节?恭喜你错了
首先,要知道内存对齐,这是求结构大小的关键方法
offsetof(),这是查数据的偏移量,知道偏移量,才能更好介绍内存对齐
#include
#include
struct P
{
char a;
int b;
double c;
}P;
int main()
{
printf("%d \n", offsetof(struct P,a));
printf("%d \n", offsetof(struct P,b));
printf("%d \n", offsetof(struct P,c));
return 0;
}
这就是结构体成员在内存中的放置位置,根据偏移量来放置
3 对齐数
对齐数是编译器默认的对齐数与该成员变量大小的较小值
如
当默认对齐数为4时,
char a; a 的大小是1字节,默认对齐数是4,那选一个较小值,那a的默认对齐数就是1
double c; c的大小是8字节,默认对齐数是4,那c默认对齐数是4;
VS默认对齐数是8
Linux下无默认对齐数
用#pragam pack()可修改默认对齐数()里面修改的默认对齐数(一般改为2的n次方)
#pragam pack ()一般放在要修改的结构体定义之前
内存对齐的规则
1 结构体内的第一个成员放在结构体变量偏移量为0的地址处
2 第二个及之后的成员变量要放到它们自己的对齐数的整数倍处
b的对齐数是4,4是4的整数倍,且偏移量为4处未被占,那b就从偏移量为4处开始放
同样,c的偏移量为1,8是1的8倍,那就从8开始放入c。
3 结构体的总大小要为最大对齐数(成员和默认对齐数中最大的那个)的整数倍(含镶嵌结构体的对齐数)
看,结构体是从0偏移量数完,占9个字节,最大对齐数是4,那4的整数倍是12,那还要再拿出
3个空间来放着构成12个字节
所以这个结构体最终大小为12
来,去vs检测一下算的对不对
4 如果成员里结构体A包含结构体B(8字节),求A大小
如果镶嵌结构体B,B的当前对齐数(B中求出来的最大对齐数就是B的当前对齐数)和默认对齐数比,求出对齐数,方法和前三条一样
A的大小是多少呢?
首先,我们上次算出struct P大小位 12 byte,最大对齐数是4 ,
int b 大小是4,对齐数4
double c 大小是8,vs的默认对齐数是8,那它对齐数就是8;
然后画图来看
欧克,vs下检验一遍
如果结构体里面有个成员是数组,像char arr[20];
他的对齐数怎么求呢?
注意不是拿20比哈
他是char类型的,1字节,默认对齐数为8,那对齐数就是1,占12个字节
其他类型的以此类推
为什么要有内存对齐,内存对齐的作用
- 平台原因
不是所有硬件都能访问到任意地址处的任意数据,某些硬件只能在某些地址处访问到特定的某种类型的数据,否则可能硬件异常,所以为了硬件方便读取,存储对齐将特定类型的数据放在特定的位置处方便硬件读取
2. 性能原因
数据结构(尤其是栈)应该尽可能的在自然边界上对齐。因为为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问
如图
总之,结构体的内存对齐是以空间换取时间
在设计结构体的时候,尽可能的将大小差不多数据类型放一堆,集中在一起,节约空间
结构体传参
结构体传参时最好传地址,函数传参时,参数是会压栈的,会有时间与空间的开销,如果传的结构体对象过大,参数压栈的系统开销比较大,会导致性能下降,所以结构体传参时最好传地址
位段
1 .what is 位段 ?
位段就是在结构体数据成员名后面加一个冒号:and 一个数字;
哦,位段的成员只能是int ,unsigned int,singed int,char
Like this picture
2.how to calculate 位段大小?
其实最简单的也就是一个sizeof就算出来了,但是还是还是必须知道这个计算逻辑的
位段的空间是按照需求以4个字节(int)或者1个字节(char)开辟的,先开辟一次,用完或者不够再开辟一次
位段冒号: 后面那个数字是所需要的比特位
来复习一下,一个字节是多少比特位,忘了把我头打爆,说明好久没学了
8个哦
好,回来继续,看题
#define A 2
#define B 3
#define MAX_SIZE A+B
struct _Record_Struct
{
unsigned char Env_Alarm_ID : 4;
unsigned char Para1 : 2;
unsigned char state;
unsigned char avail : 1;
}*Env_Alarm_Record;
int main()
{
struct _Record_Struct* pointer = (struct _Record_Struct*)malloc(sizeof(struct _Record_Struct) * MAX_SIZE);
return 0;
}
字有点多,但全是精华。
malloc开辟了多大的空间;
首先,第一个是char 类型,先开辟一个字节,一个成员需要4个比特位,那还剩4个,第二个成员需要2个比特位
还剩下两个,第三个成员是结构体成员,需要一个字节,直接开辟,目前两个字节了
最后一个成员需要一个比特位,之前还剩两个比特位,但由于已经新开辟了一个字节
数据在内存中是按顺序放的,所以只能再开辟一个字节,占一个比特位,
还剩7个
总共开辟了三个字节,这个结构体占3字节sizeof(struct _Record_Struct)== 3,之后是预处理,直接
替换3*A+B ,最后算出来9字节,所以开辟了9个的大小空间
3.what's meaning for 位段?
首先,一定程度上节省空间,列如
struct A
{
int a:2; //
}
当a存储的数据数据状态只为:00,01,10,11
只需要两个比特位就能保存,但一个整型4字节,32比特位过于浪费,所以就可以用位段
像年龄,分数这样用位段就挺好
其次,数据的传输,用位段很好节省空间和时间
但是,位段有一定缺点 跨平台问题
1 int位段的数据被当成有符号数还是无符号数是不是确定的
2 位段在不同机器下的最大位的数目是不能确定的,在16位机器下一个int最大是16位,32位机器下最大位是32位
如果在冒号后写了个24这样的数,在32位机器下没问题,但到了16位机器就会处问题
3 位段成员在内存中的数据分配是从左到右,还是从右到左这是尚未定义的,不知的,取决于编译器,
注意!!!这不是大小端字节序,大小段是在以字节位单位情况下看的,既然这厮位段,位那就是比特位,所以与
大小端区分开
4 当一个结构包含两个位段时,第一个位段成员比较小,第二个比较大,当第一个成员使用一定内存后,还剩一定位,
再开辟新的位容纳第二个,之前的剩余的位是使用还是舍去,这是不确定的。
总之,与结构相比,位段可以达到同样的功能和效果时,位段可以很好节省空间,但是有跨平台的问题。
枚举类型
1.定义
这个东西应该算自产自销吧。。
列如
enum Sex
{
MALE,
FEMALE,
SECRET //最后一个枚举变量后面不打,
};//要有分号
枚举里面的内容是有一定值的,默认从0开始,一次增加1
像
枚举最开始可以在枚举体里面,给枚举变量赋初值的,之后出去就改不了
如:
2.枚举的优点
增加代码可读性和可维护性
与#define定义的标识符相比他是有类型的,更加严谨
便于携带调试,在预处理时,#define定义的标识符直接被替换了,调试时就已经换成对应的值了
使用方便,一次可定义多个常量
联合体(共用体)
1.定义
这种自定义类型也包含一系列成员,这些成员共用一块空间
看看这个代码
联合体成员共用最大的那个空间,他在同一时间内只能用一个。
判断当前机器为是否大小段
用联合体来判断
int check_sys()
{
union Un
{
int i;
char c;
}u;
u.i = 1;
return u.c;
}
2.联合体大小的计算
联合体的大小至少是最大对齐数的整数倍
当最大成员的大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍;
如