结构体内存对齐,位段,枚举相关问题.
迪丽瓦拉
2025-05-30 17:11:35
0

本文标签:    结构体内存对齐 结构位段问题 枚举

文章目录

目录

文章目录

一、结构体的内存对齐.

1.计算结构体大小

2.为什么存在内存对齐?

二、结构体的位段问题.

三、枚举

1.枚举类型定义

2.枚举的优点

为什么使用枚举?

总结


一、结构体的内存对齐.

1.计算结构体大小

知道了结构体的基本使用,我们可以深入探讨一个问题:计算结构体的大小.

这也是一个热门的考点:结构体内存对齐.

示例代码:

 按照我们以往的理解, char 占一个字节, int 占四个字节, 应该是 6 个字节的大小,但是编译得到的结果是 12 个字节.由此我么引入了 结构体的内存对齐知识点.

结构体的成员在内存中存储是有自己的规则的,规则如下:

1.第一个成员在与结构体变量偏移量为 0 的地址处;
2.其他成员变量要对齐到 对齐数 的整数倍的地址处;


对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值.( vs 默认的值为 8 .)


3.结构体总大小为对齐数中最大的那个对齐数 ( 每个成员变量都有一个对齐数 ) 的整数倍;
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处 , 结构体的整体大小就是所有最大对齐数( 含嵌套结构体的对齐数 )的整数倍.

  1. 第一句:示例代码中第一个成员 c1 .它相对于起始位置的偏移量就是 0.(也就是结构体的第一个成员就是从 0 偏移量的地址处开始存放.)此时 c1 占 1 个字节.

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.性能原因 : 数据结构(尤其是栈)应该尽可能地在自然边界上对齐 , 原因在于:为了访问未对齐的内存 , 处理器需要作两次内存访问 , 而对其的内存访问仅需要一次访问.


总体来说 : 结构体内存对齐时拿空间来换取时间的做法.

二、结构体的位段问题.

位段的声明和结构是类似的 , 有两个不同:

  1. 位段的成员必须是:int,unsigned或signed int.
  2. 位段的成员名后面有一个冒号和一个数字.

示例代码:

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,使用方便,一次可以定义多个常量.


总结

  • 结构体内存对齐
  • 位段问题
  • 枚举

抽空水的文章,看到这了给个三连呗.如有不足,还望指出.

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...