目录:
结构体的初始化
结构体的内存对齐
位段
-->位段的内存分配
-->位段的跨平台问题
枚举的优点
联合的特点
联合体的大小计算
结构体的初始化
struct Stu
{
char name[20];
int age;
char sex[5];
char id[20];
};//注意这个分号不能丢
定义一个结构体这个结构体中有四个元素分别为:name;age;sex;id
对结构体初始化:
struct Stu a = { "张3",4,"男","1800" };
结构体的嵌套初始化:
struct Dodo
{
struct Stu b;
struct Dodo* next;
};
int main()
{
struct Dodo chx= { {"王5",5,"公","123123"},NULL };
return 0;
}
结构体的内存对齐
结构体的对齐规则:
1.结构体的第一个成员在结构体变量偏移量为0的地址处
2.其他成员变量要对齐到 某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认的一个对齐数 与 该成员大小的较小值
3.结构体的总大小为最大对齐数(每个成员变量都有对齐数)的整数倍
4.如果嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是
就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍
---------------------------------------------------------------------------------------------------------------------------------
例题Top1:
struct S2
{
char c1;
char c2;
int i;
};
1.分别判断结构体中每个成员的对齐数 int 对齐数为4,char 对齐数为1,vs中默认对齐数为8
--> char c1在内存中占用一个字节,对齐数->1,且为结构体的第一个成员要放在偏移量为0的位置
--> char c2在内存中占用一个字节,对齐数->1,偏移量跟对齐数都是1,所以紧接着内存
--> int i在内存中占用4个字节,对齐数->4,要在偏移量为4的倍数中开始
数一数一共占用的8个框框也就是8个字节,结构体成员中的最大对齐数(int)为4,8是4的倍数,
所以结构体的大小为 sizeof(struct S2)值为 8
例题Top2:
struct S3
{
double d;
char c;
int i;
};
1.分别判断结构体中每个成员的对齐数 int 对齐数为4,char 对齐数为1,double 对齐数为8 vs中默认对齐数为8
--> double d在内存中占用8个字节,对齐数->8,且为结构体的第一个成员要放在偏移量为0的位置
--> char 1在内存中占用1个字节,对齐数->1,偏移量和对齐数都是1紧接着存
--> int i 在内存中占用4个字节,对齐数->4,但char后的偏移量为9,不是4的倍数所以int i要放在偏移量为12的位置,那么如图所示struct S3的大小为15,但是实际上struct的大小要是成员中最大对齐数的倍数 也就是double 8 的倍数,所以最终的大小sizeof(struct S3)为16
Q1:为什么会存在内存对齐呢
A1:平台原因->不是所有硬件平台都支持访问任意地址上的数据的
性能原因->数据结构应该尽可在自然边界上对齐(尤其是栈),对齐的内存仅需一次访问
总结:结构体的内存对齐是拿空间来换取时间
---------------------------------------------------------------------------------------------------------------------------------
位段
Q1: 什么是位段
A:位段的成员必须是int unsigned int 或 signed int, char其实也行
位段的成员名后有一个冒号和一个数字。
位段可以根据需求来分配空间,某些成员并不需要32个bit位 ,位段是不对齐的
-->位段的空间上是按照四个字节(int)或者一个字节(char)的方式来开辟的
-->位段涉及很多不确定因素,所以位段是不跨平台的
struct a
{
//先开辟32个字节
int a : 2;//32-2=30
int b : 5;//30-5=25
int c : 10;//25-10=15
int d : 30;//15<30,再开一个32个字节的空间 32-30=2
};
-->先开辟32个字节,a分配2后剩下30,b分配后剩25,c分配后剩下15 此时剩下15不够开辟d
那么再开32个字节来存放d
-->c语言中并没有明确规定d会不会使用上一次所开辟空间的剩余所以这也是不能移植的原因
-->位段是时间换空间的做法
1.int 被当做有符号数还是无符号数是不确定的
2.位段中最大位的数目不能确定。(16位最大16,32位最大32,写成28在16位机器上就会有问题)
3.位段中的成员从左向右分配还是从右向左分配没有明确的定义
4.当一个结构包含两个位段,第二个成员比较大的话,无法容纳第一个位段的空位时,是舍弃空位还是利用这事不确定的
---------------------------------------------------------------------------------------------------------------------------------
枚举的优点
Q1:我们可以用#define定义常量,为什么要用枚举?
枚举的优点:
1.增加代码可读性和可维护性
2.和#define 定义标识符比较 枚举有类型检查更加严谨
3.为了防止命名污染
4.便于调试,使用方便一次可以定义多个常量
看代码段中的注释应该能找到规律:
enum Day
{
fir,//为0
roc,//为1
top = 5,//为5
top2,//为6
am = 200,//为200
ak//为201
};
联合的特点
联合的成员是共用同一块内存空间,这样一个联合体的大小,至少是最大成员的大小(因为联合至少得有保存最大的那个成员的能力)
可以用共用体来判断大小端的存储:打印1的话那么就是小端存储
int test()
{
union
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
int main()
{
int a = test();
printf("%d", a);//打印的是1
return 0;
}
把联合体中u.i赋值1那么在内存中是01 00 00 00,当你返回u.c时由于u.c是char型只能访问一个字节,所以就会把01(十进制的1)传回去
--------------------------------------------------------------------------------------------------------------------------------
联合体的大小计算
-->联合体的大小至少是最大成员的大小
-->当最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
按照这么来说联合体Un的大小应该为5,但是5并不是最大对齐数4的倍数,所以实际上Un的大小为8