本文标签: 结构体内存对齐 结构位段问题 枚举
文章目录
目录
文章目录
一、结构体的内存对齐.
1.计算结构体大小
2.为什么存在内存对齐?
二、结构体的位段问题.
三、枚举
1.枚举类型定义
2.枚举的优点
为什么使用枚举?
总结
一、结构体的内存对齐.
1.计算结构体大小
知道了结构体的基本使用,我们可以深入探讨一个问题:计算结构体的大小.
这也是一个热门的考点:结构体内存对齐.
示例代码:
按照我们以往的理解, char 占一个字节, int 占四个字节, 应该是 6 个字节的大小,但是编译得到的结果是 12 个字节.由此我么引入了 结构体的内存对齐知识点.
结构体的成员在内存中存储是有自己的规则的,规则如下:
1.第一个成员在与结构体变量偏移量为 0 的地址处;
2.其他成员变量要对齐到 对齐数 的整数倍的地址处;
对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值.( vs 默认的值为 8 .)
3.结构体总大小为对齐数中最大的那个对齐数 ( 每个成员变量都有一个对齐数 ) 的整数倍;
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处 , 结构体的整体大小就是所有最大对齐数( 含嵌套结构体的对齐数 )的整数倍.
2.第二句: vs 平台默认对齐数是 8,也就是用 8 和当前的 i (整型 4 个字节)比较,取较小值就是 4 .然后根据对齐规则,要将第二个变量的地址存放到对齐数 4 的整数倍的地址处,4 的整数倍地址就是 4 ;
而下一个变量 c2 占 1 个字节大小,将它与对齐数 8 比较,取较小值 1 也就是 c2 的对齐数,将 c2 从 1的整数倍处存放一个字节的内存.所以当前 c1,c2 i 一共占 9 个字节的空间
3.第三句:要算最终结构体的大小,就要算出每个结构体成员变量中的最大对齐数的整数倍. c1,i,c2的对齐数分别是 1,4,1 ,最大对齐数就是 4 ,所以结构体的总大小就要是 4 的整数倍,而当前结构体大小是 9 ,要使它是 4 的整数倍,就要再浪费掉 3 个字节的空间,最终就会得到结构体总大小就是 12 .
例题:计算一下结构体的大小
struct s1{char c1;int i;double d;};
首先 c1 存放在 0 偏移量处, 然后 i 的对齐数是 4 ,所以要存放到 4 的整数倍处,占 4 个字节,然后变量 d 的对齐数是 8 ,当前地址也是 8 ,所以向下存放 8 的字节的空间.现在一共占 16 个字节的空间, 16 是所有变量的最大对齐数 8 的倍数,所有这个结构体大小就是 16 .
4.第四句:可以结合以下代码理解.
根据第四个规则,首先将 c1 存放到 0 偏移量处,第二个结构体成员也是一个结构体,它的大小刚刚计算过了是 16 ,它如果要开始存储,就要对齐到它本身结构体成员中最大对齐数的整数倍处,它的成员最大对齐数是 8 ,所有要从 8 的整数倍地址处开始存放 16 个字节;
现在的偏移量是 24 它是 d 对齐数 8 的整数倍,再向下存放 8 个字节的空间.现在整体的总大小是 32 ,根据规则,它当前的总大小要是所有变量(包括嵌套的结构体成员变量)的最大对齐数的整数倍,如果不是则要继续浪费空间,而当前嵌套结构体的最大对齐数是 8 ,32 是 8 的整数倍,所有这个嵌套结构体的总大小就是 32 .
2.为什么存在内存对齐?
为什么存在内存对齐?
1.平台原因 : 不是所有的硬件平台都能访问任意地址上的任意数据的 , 某些硬件平台只能在某些地址处取特定的类型数据 , 否则抛出硬件异常.
2.性能原因 : 数据结构(尤其是栈)应该尽可能地在自然边界上对齐 , 原因在于:为了访问未对齐的内存 , 处理器需要作两次内存访问 , 而对其的内存访问仅需要一次访问.
总体来说 : 结构体内存对齐时拿空间来换取时间的做法.
二、结构体的位段问题.
位段的声明和结构是类似的 , 有两个不同:
位段的成员必须是:int,unsigned或signed int.位段的成员名后面有一个冒号和一个数字.示例代码:
struct S{int a : 2;int b : 5;int c : 10;int d : 30;};
只要在结构体成员变量的后面加上冒号和一个数字就是位段.
代码的意思是 a 成员占 2 个 bit 位,b 占 5 个 bit 位, c 占 10 个 bit 位, d 占 30 个 bit 位.那位段成员是怎么分配空间的呢?由此引入以下概念:
位段的内存分配:
1 . 位段的成员可以是int, unsigned int ,signed int或者是char(属于整型家族)类型.
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的.
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段 .
也就是说如果位段中的类型是 int ,它就是一个整型一个整型的来开辟空间,首先 a 开辟一个 int 的空间也就是 4 个字节,开辟 32 个 bit 的空间,而位段定义 a 只占 2bit 的空间,还剩下 30 个 bit ,而 b 的 5 个 bit 也会存放到 a 剩下的 30 个 bit 中,就还剩下 25 个 bit 位,接着 c 占 10 个 bit ,还剩 15 bit, 而 d 需要 30 个 bit ,而剩下的字节不够存放 d ,就会再开辟一个 4 字节的空间.所有就可以求出这个位段的大小是 8.
在上面的代码中, c 成员用完 10bit 的后还剩下的 15 bit 不够存放 d 的 30bit ,所以要再开辟 4 个字节.但是那剩下的 15 bit是 d 用了再开辟 4 字节再用 15bit ,还是不用剩下的 15bit 只在新开辟的 4字节中存放?
这些因素在C语言中是不确定的,是不跨平台的,在不同的平台上可能实现是不一样的,所以要使用位段要先研究当前平台的规定,写出针对不同平台的代码.
位段的跨平台问题:
1.int位段被当成有符号数还是无符号数是不确定的.
2.位段中最大位的数目不能确定(16位机器最大16,32机器最大32).
3.位段中的成员在内存中从左向右分配,还是从右向左标准未定义.
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃位重新开辟还是利用,也是不确定的.
结论:位段和结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题.
三、枚举
1.枚举类型定义
枚举:
1.虽然枚举类型定义和结构体的类型定义很相似,但是他们俩是不一样的.
结构体:
2. 既然知道枚举类型是常量,那常量必然是有值的,我们可以将它的值打印出来.
打印枚举常量的值:
结论:这些常量的值是依次递增 1 的方式往下走.(默认是增长1).
3.既然是常量但是我们不能对它进行赋值,假设对枚举类型的常量赋值一个 int ,编译器就会报错.
因为枚举是一个类型,我们不能将类型赋值为另一个类型.
4.如果在枚举类型中给它的常量赋值(这里是在给它枚举类型中的常量赋值),它的下一个常量的值仍然是在当前常量的值的基础上递增 1.
2.枚举的优点
为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举? 枚举的优点:
1.增加代码的可读性和可维护性.
2.和#define定义的标识符比较枚举有类型检查,更加严谨.
3.防止命名污染.
4.便于调试.
5,使用方便,一次可以定义多个常量.
总结
结构体内存对齐位段问题枚举
抽空水的文章,看到这了给个三连呗.如有不足,还望指出.