1.结构体
1.1结构体的声明
//结构体的每个成员可以是不同的类型
struct 结构体名
{
成员列表
}变量列表; //变量列表可有可无
例如,描述日期:
■特殊的声明(匿名声明):该结构体只能访问一次
省略了结构体名。只能在结构体声明时使用,别处无法访问。
*p=&b1;编译器会将这条语句判错。虽然两个结构体的成员列表一致,但在编译器看来b1和*p属于两个不同的结构体。
1.2结构体的自引用
在结构中包含一个类型为该结构本身的成员
比较两个代码段,1是错误的。结构体内只能包含同类型的结构体指针,不能包含同类型的结构体变量。且使用时不能用匿名结构体,应写为:
1.3结构体变量的定义和初始化
1.3.1结构体变量的定义
可在声明结构体类型的同时或者声明后再定义。
1.3.2结构体变量的初始化
初始化:定义变量的同时赋初值
初始化时也可不按照结构体内成员的声明顺序来初始化。在要赋的值前加".成员名"便可实现初始化。
1.4结构体内存对齐
请看结构体S1和S2包含的成员是一致的,那么在计算sizeof时你是否会根据字符型数据占1个字节,整型占4个字节而计算出结果为6呢?我们来看一下运行结果。
为什么会出现这种结果呢?这是因为结构体的内存对齐。
首先我们看一下内存对齐的规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。
//VS中默认的值为8
//Linux中没有对齐数
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
结合刚开始给出的例子,画出下图(上图为S1的成员在内存中开辟的空间的位置,下图为S2成员在内存中开辟的空间的位置)。
S1所占空间为9个字节,根据内存对齐的规则3,结构体总大小为最大对齐数的整数倍,对于结构体S1,其三个成员变量中最大对齐数为4,所以S1的总大小应为12字节;同理,图中S2占6字节,其成员变量中最大对齐数为4,那么S2的总大小为8字节。
由于存在内存对齐,所以对于拥有相同成员变量的结构体而言,不同的成员声明顺序会导致结构体所占空间大小不同。
为什么存在内存对齐?
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
即结构体的内存对齐就是拿空间换时间的做法。
可以通过预处理指令#pragma pack() 自己修改对齐数,方式如下:
1.5结构体传参
如上代码,结构体传参可将结构体作为参数传递过去,也可将结构体的地址作为参数传递过去,猜想哪种方法更好一些?
答案是应选函数print2()的传址方式,因为函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
所以结构体传参时尽量选择传结构体的地址。
2.位段
2.1什么是位段
位段与结构体的声明类似,有两处不同:
1.位段对成员的类型有要求。其成员只能是int,unsigned int或signed int;
2.成员名后要加上冒号和数字。(数字表示变量所占的比特位数,其值不能超过该变量类型的大小)
例如,结构体S就是一个位段类型。
2.2位段的内存分配
1. 位段的成员可以是 int、unsigned int、signed int 或者是 char (属于整形家族)类型。
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。 若成员为int型,则以4个字节的方式开辟,若成员为char型,则以1字节的方式开辟。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
举个例子:
最终位段变量s在内存中的二进制序列为0000 1100 0000 0110 0000 1000 0000 1001
转换位16进制在内存中即为0C 06 08 09。通过vs进行调试验证结果。
所以位段的内存是如何开辟的呢?
1.因为位段中的成员类型为char型,所以空间上是按照1个字节来开辟的,而位段 S 类型中第一个成员定义的大小为 5 bit, 在赋值时将 a 赋值为12,10的二进制序列为:0000 1100 ,受定义大小的限制,需舍弃一部分序列再存入内存中,所以最终存入内存中的序列为:01100,且规定在分配的内存中是从后向前放入序列(如上图红色方框)。
2.当成员 a 放入内存中后,第一个字节还剩下3个bit位,而第二个成员占4个bit位,剩下的内存不能放下成员 b,所以新开辟1个字节的空间用来存放成员b,按照步骤1的方式依次放入;
3.若放入前一个成员后该字节所剩的bit位还能放入下一个成员,则将其继续按从后向前放入序列,否则就开辟新的空间依次存放,最终整个位段所占的内存就为4个字节大小。
结果如图:
总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。