当前位置: 首页 > news >正文

结构体,枚举,联合大小的计算规则

目录

1.结构体大小的计算

补充(位段)

2.枚举的大小(4个字节)

3.联合大小的计算


1.结构体大小的计算

(1)结构体内存对齐的规则

1. 第一个成员在与结构体变量偏移量为 0 的地址处。


2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的对齐数 与 该成员大小的 两者之间的较小值
VS 中默认的值为 8     gcc没有默认对齐数,那么对齐数就是该成员的大小


3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。


4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

例如

struct S1
{char c1;//大小为1,默认对齐数为8,取其小就是1,所以其对齐数是1int a;//大小为4,默认对齐数为8,取其小为4,所以其对齐数就是4char c2;
}int main()
{struct S1 s1={0};printf("%d\n",sizeof(s1));
}

 其内存分配如图所示

 其中的成员占了9个内存单元,但是结构体的内存大小是其最大对齐数的整数倍,这里的最大对齐数是4,所以在9以上的,最小的,4的倍数是12

再来一例,与上面同理,直接看图

struct S2
{char c1;char c2;int a;
}int main()
{struct S2 s2={0};printf("%d\n",sizeof(s2));
}

 成员的内存总共为8个内存单元,刚好也是4的倍数

可以看到两个结构体的成员是相同的,但是后者占用内存比前者少,所以总结起来

将占用内存小的成员写在前面 ,这样能更加高效的利用空间

(2) 那么既然有空间的浪费,为什么不紧密的存放每一个成员呢,内存对齐的意义

1. 平台原因 ( 移植原因 ) :
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因 :
数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说: 结构体的内存对齐是拿空间 来换取 时间 的做法。
 

 (3)修改默认对齐数

结构在对齐方式不合适的时候,可以自己更改默认对齐数。

#pragma 这个预处理指令,可以改变默认对齐数,例如

#pragma pack(1)设置默认对齐数为1,这样每一个结构体成员都是紧密排列的了

#pragma pack(1)//设置默认对齐数为1
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

补充(位段)

位段的内存分配
1.位段的成员可以是intunsignedint signedint或者是char(属于整形家族)类型

2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的

3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

struct S
{int a;int b;int c;int d;
};

按照上面的结构体的计算规则,那么这个结构体的大小为16

对于位段是8个字节

struct S
{int a:2;//冒号后的数字表示a只需要2个比特位int b:5;int c:10;int d:30;
}
//总共需要47个比特位,那么6个字节足够(6*8)=48,计算的结果却为8个字节

 注意位段是按整型大小的字节进行开辟的

注意:

struct S
{int a:2;int b:5;int c:10;int d:33;//不能超过32个比特,报错:d的位域大小无效
}

 本来int类型需要占用32位空间,现在只需要占用2个比特位的空间,位段的使用节省了空间大小

 举例

struct S
{char a:3;char b:4;char c:5;char d:4;
}int main()
{struct S s={0};s.a=10;//二进制序列1010s.b=20;s.c=3;s.d=4;return 0;}

 首先struct S s={0};所以先给s分配8个比特位

 如上图所示a只有3个比特位,但是s.a=10//二进制序列为1010,所以内存中只能存3个比特位,即1010中的010

s.b=20//二进制序列10100占5个比特,但是b只有4个比特,所以只能传“0100”

s.c=3//二进制序列011占3个比特,但是c有5个比特,前面补0,即“00011”

s.d=4//二进制序列为100,但是d占4个比特,前面补0,即“0100”

 所以总体写下来如图所示

阅读内存时,内存是16进制数字,则四个2进制位为一个16进制,从左往右依次读

0010 0010 0000 0011 0000 0100

2       2       0       3       0       4   

位段的跨平台问题

1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。
( 16 位机器最大 16 , 32 位机器最大 32 ,写成 27 ,在 16 位机器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

总结

跟结构相比,位段可以达到同样的效果,可以很好的节省空间,但是有跨平台的问题存在

应用:用位段封装网络上传输的数据包等

2.枚举的大小(4个字节)

enum Color//颜色
{RED=1,GREEN=2,BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;
enum Sex
{MALE,FEMALE,SECRET
}int main()
{enum Sex s=MALE;printf("%d\n",sizeof(s));return 0;	
} 

结果为4

3.联合大小的计算

(1)计算规则

联合的大小至少是最大成员的大小。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

union Un
{int a;//成员大小为4,默认对齐数为8,对齐数为4char arr[5]; //成员大小char是1,默认对齐数是8,得到对齐数是1,不要拿数组的大小进行计算
}int main()
{union Un u;printf("%d\n",sizeof(u));return 0;
}

 如果联合体如下图所示

那么其占用内存总大小为5个内存单元,但是联合的大小

至少是最大成员的大小。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

5(最大成员大小)不是(最大对齐数)4的整数倍,所以应该按下图进行内存分配

如图浪费了3个内存单元,使得总内存为8个字节,为最大字节数(4)的整数倍 

 

http://www.lryc.cn/news/108117.html

相关文章:

  • Vue2 第十七节 Vue中的Ajax
  • ES6 - 字符串新增的一些常用方法
  • 最新SQLMap安装与入门技术
  • Java 使用 Google Guava 实现接口限流
  • 帮助中心的价值是什么?怎样才能在线搭建官网网站帮助中心?
  • Kubernetes——理论基础
  • 【VUE3】
  • 《金融数据保护治理白皮书》发布(137页)
  • 上海亚商投顾:沪指震荡微涨 金融、地产午后大幅走强
  • Linux文件管理知识:查找文件
  • 【TypeScript】安装的坑!
  • spring boot 2.x 使用 jpa 映射 json mysql列数据映射乱码
  • 创建Helm脚本
  • 2.05 购物车后台刷新并显示
  • 2023年第四届“华数杯”数学建模思路 - 案例:异常检测
  • inline的盒子设置transform不生效
  • 自然语言处理学习笔记(四)————词典分词
  • jsoncpp库和nlohmann-json库实现JSON与字符串类型转换
  • 20230803 函数传参引用
  • IDEA SpringBoot项目引入外部jar并打包
  • ModaHub魔搭社区——阿里云通义千问宣布开源!70亿参数模型上线魔搭社区,免费可商用
  • Jenkins 自动化部署实例讲解,另附安装教程!
  • arcgis字段计算器
  • 数据结构: 线性表(无哨兵位单链表实现)
  • Exploring the Underlying Architecture of CSS3
  • 方差分析||判断数据是否符合正态分布
  • java linq多字段排序时间比较
  • 【c++】rand()随机函数的应用(二)——舒尔特方格数字的生成
  • “深入剖析JVM内部机制:探索Java虚拟机的运行原理“
  • pandas 新增数据列的几种方式