目录
一.结构体
1.什么是结构
我们已经了解很多种类型来声明定义不同种类的数据,有int,float,double等等,但是假如要刻画工厂的一颗螺丝钉,我们该用什么类型进行修饰呢?
仔细想想,即便用数组也不好刻画,因为数组中的元素都具有相同的类型,而想要刻画一个螺丝钉,需要不同的类型进行定义,需要它的编号(number),大小(size),现有数量(on_hand),名称(name).
2.结构的声明
我们可以根据这个定义我们螺丝钉的声明,它的struct tag(结构体类型名)是struct tag,类似int,double等等,它包含的member-list有4个(有size,on_hand,number,name).
然后可以选择创建或不创建variable list(变量列表),如果在声明时同时创建variable list,此时创建
的结构体变量是全局变量,而如果没有创建,只要有相应的struct tag,创建变量还不是手到擒来的
事.
3.特殊的声明
每个结构代表一个新的作用域,每个结构都为它的成员变量分辟了相应的空间.
因此,上述两个结构体中number和name重复出现是没有问题的,两者分别有着它自己的空间.
但是,假如在声明的时候,没有加相应的标签(tag)
此时,编译器会把它理解为依旧是两个不同的结构体,即便它们所有的元素都相同.
4.结构体的自引用
假设我们想要构造一种新的数据结构——链表,它的实现是由一个结构体,指向另一个新的结构体.
我们写出这样的代码可行吗?
答案显然是否定的,假设存在这样一个结构体,那用sizeof计算它的大小,它的大小将会是无穷大,结点中又有另一个t同样的结点.
一个比较可行的方案是,放一个结构体指针,而这个指针可以指向新的结构体.
然后为了简便程序,不用创建变量,都需要输入struct Node,我们常常会用typedef进行重命名.
5.结构体的定义和初始化
我们可以直接创建一个全局的结构体变量s1,或者在主函数中创建临时结构体变量s2.
用一个中括号按顺序进行赋值即可.
如果不想按顺序进行赋值,可以指定初始化.
指定初始化有以下两个优点:
1.易读且容易验证 2.可以不需要按顺序进行赋值
初始化中没有涉及到的成员默认为0.
假设一个结构体中包含另外一个结构体,初始化时,只需在中括号{}再加上另一个中括号{},初始化即可.
6.结构体的操作
访问结构体的成员变量,用.
假如是结构体指针,要想访问结构体中的成员变量,C语言提供了另一个操作符 —>
结构的另一种主要操作是赋值运算
虽然赋值操作仅适用于类型兼容的结构.但数组不能赋值操作,而结构体可以,属实是意外之喜.
除了memcpy与strcpy外,我们现在又知道另一种方法进行赋值操作,在结构体内放一个数组,则嵌在其中的数组在赋值操作的同时,也产生一个新的同样大小的数组空间.
7.结构体内存大小计算
1.四大规则
假设拿这样一个结构体做例子,对规则进行介绍.
(1)结构体的第一个成员直接对齐到相对于结构体变量起始地址为0的地方
(a所占大小为1个字节,从起始地址为0的地方开始存储)
(2)其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
(VS默认对齐数为8,Linux系统无对齐数)
(x占4个字节,比对齐数8要小,因此x的对齐数为4,偏移量为1,2,3的空间不是4的整数倍,会被浪费,然后从偏移量为4的地方开始存储x)
同样,y占8个字节,VS对齐数也为8,所以y的对齐数就是8,而刚好此时偏移量为8的整数倍,所以可以顺着继续存储.
(3)如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
![](https://i-blog.csdnimg.cn/blog_migrate/73dc05b7395ad8b0340986086fedbd6b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2f70a299cccdf8814119ab4178736a80.png)
2.修改默认对齐数
8.结构体传参
我们知道形参是实参的一份临时拷贝,假如结构体传参,大多数情况都是传函数体指针给函数就
好,否则假如结构体本身占据的空间很大,参数压栈的的系统开销比较大,容易导致性能的
二.位段
1.位段的定义
有些时候,我们定义一个变量a,为int 类型
但是我们知道,虽然作为一个整型,但实际上变量a取到的范围根本不需要4个字节,仅仅只需要
3个bit位表示范围即可,为了应对这种情况,进一步节省空间,位段便孕育而生.
和结构体一样,位段有着相似的声明方式.
A便是一个位段,位段和结构体的区别,主要在以下两点
(1)位段的成员必须是int,unsigned int或者char类型
(2)位段中的位指的是二进制位,为了调整变量所占空间的大小,每个成员变量后面都要加一个:,
以及它所占的对应比特位(二进制位).
2.位段所占空间大小计算
位段具体如何存储数据我们并不知道,但我们知道数据在vs2019编译器底下,是小端存储,即低位放在高地址,高位放在低地址.
所以我们可以画出下面这幅图:
假设从低位往高位开始存储数据,7的二进制为1 1 1,刚好可以用3个比特位表示
紧接着是3,0 0 1 1,可以用4个比特位准确表示,则继续存进去0 0 1 1
这时候_c申请5个比特位,已经超过8个字节,这时候便又申请了一个字节,占6个比特位,注意剩余还有1个比特位,依旧占用
存进去的12的二进制表示1 1 0 0,继续接_d,5的二进制1 0 1存入.
0 0 0 1 1 1 1 1 1 0 1 0 0 1 1 0
对应的十六进制1f a6 00 00.
而实际上位段存在跨平台问题,所以,我们在vs测试也只能大致猜想位段是如何存储的.
3.位段的具体运用
32位IP地址的划分利用.
三.枚举
1.定义
![](https://i-blog.csdnimg.cn/blog_migrate/7c7a2178ce44e17cbb800880bdd53d12.png)
这些可能取值都是有值的,默认从0开始,一次递增1.
2.枚举的优势
四.联合
1.联合的定义
联合和结构体声明也是类似的
联合最大的特点在于这些成员公用同一块空间(所以联合也叫共用体).