结构体
声明
struct Student
{
char name; //姓名
int age; //年龄
char sex[5];//性别
}; //分号不能丢
初始化
定义结构体变量p,并且初始化
Student p={"博雅",19,"女"};
指定顺序初始化
Student s={.age=15,.name="张三"};
结构体的特殊声明
但是匿名结构体类型只能用一次,在声明时省略了标签(tag)
结构体自引用
可以包含一个同类型的结构体指针
typedef类型重命名,将struct node——node
也可以这样写
但是匿名的结构体类型不能实现结构体自引用是效果
结构体内存对齐
计算结构体的大小
对齐规则
- 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
- 其它成员要对齐到某个数字(对齐数)的整数倍地址
对齐数=编译器默认的对齐数 与 该成员变量大小的较小值(VS默认的值为8,Linux中gcc没有对齐数,对齐数就是成员自身的大小)
- 结构体的总大小是最大对齐数(结构体每一个成员都有一个对齐数)的整数倍
- 如果嵌套了结构体情况,嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数的整数倍
练习一
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));//12
所以i要对齐到4的倍数的地址处,也就是4,所以i从4开始存起。
c2的大小是1个字节,VS默认是8,他两的较小值是8,所以对齐数数1,所以c2只占一个字节,所以加起来为9
c1的对齐数是1,i的对齐数是4,c2的对齐数是1,所以最大对齐数是4,
而9不是4的倍数,所以我们结构体总大小是12(4×3=12)
练习二
struct S2
{
char c1;//1 8——1
char c2;//1 8——1
int i; //4 8——4
};
printf("%d\n", sizeof(struct S2));
c1占1个字节,c2也是1,但是i的偏移量要是4的整数倍,所以必须是4开始,1+3+4=8个字节
练习三
struct S3
{
double d;//8 8——8
char c;//1 8 ——1
int i;// 4 8——4
};
printf("%d\n", sizeof(struct S3));
i从4的整数倍的地址开始,所以是从12开始往下4个字节。15不是8的整数倍,所以16是结构体的总大小
练习四:结构体嵌套问题
struct S3
{
double d;//8 8——8
char c;//1 8 ——1
int i;// 4 8——4
};
struct S4
{
char c1;//1 8——1
struct S3 s3;//成员中最大对齐数是8,从8的倍数开始,s3大小是16个字节
double d;//8 8——8
};
printf("%d\n", sizeof(struct S4));
因为所有最大对齐数是8,所以内存是8的倍数,32是8的倍数,所以结构体大小是32
如果结构体中有数组怎么办?
char c1[5]按照5个char存即可,也就是5个字节
int c1[5]按照5个int存即可,就是4×5=20个字节
为什么要内存对齐
提升时间,拿空间换时间的做法
修改对齐数
#pragama可以改变编译器的默认对齐数
直接访问
也可以传递地址,间接访问
结论:结构体传参的时候,尽量传结构体的地址
结构体实现位段
位段(二进制位)的声明和结构是类似的,有两个不同
专业用来节省内存的,加上数字后,规定了a占2个bit位,b占5个bit位,c占10个bit位
- 位段的成员必须是
- 位段的成员名后面有一个:和数字
结构体 位段
%zd打印无符号整数
位段的内存分配
- 位段成员可以是int、unsigned int、signed int或者是char等类型
- 尾端的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应避免使用位段
- 申请到的一块内存中,从左向右使用,还是从右向左使用,是不确定的,VS编译器里面是从右向左使用
- 剩余的空间不组织下一个成员使用的时候,是浪费还是使用呢?在VS中是浪费
首先申请1个字节(char),a占3个bit,b占4个bit,c要占5个bit但是空间不够,直接浪费,再往后申请1个字节
所以可知s占3个字节
转换成16进制
a中存10:0001010
b中存12:0001100
c中存3: 0000011
d中存4: 0000100
位段的跨平台问题
- int位段被当成有符号还是无符号数是不确定的
- 位段中最大的树木不能确定
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
位段
不能对位段式结构的成员直接取地址
只能先放在一个变量中,然后再赋值给位段成员