【C语言】——结构体进阶:结构体的内存对齐(超详细)

2023-05-16

前言:

        上一篇已经讲了结构体的基本用法。相信各位小伙伴以经学会怎么使用。但是还有一个问题没有弄明白——结构体到底多大,占内存空间多不多,以经系统到底怎么访问结构体内的数据的。

        接下来,详细分析一下结构体的内在……

目录

1.修炼内功,掌握结构体的内存对齐

1.1结构体对齐规则四步走:

2.为什么要会存在内存对齐

2.1怎么修改默认对齐数

3.结构体传参

4.叠甲


1.修炼内功,掌握结构体的内存对齐

        上篇已经讲述了结构体的基本使用,现在来深入探究下一个问题:计算结构体的大小。这个问题也会经常出现在各个大厂的笔试题目中,即——结构体对齐。

        话不多说。直接在下面的代码中感受下:

int main()
{
	//练习1
	struct S1
	{
		char c1;
		int i;
		char c2;
	};
	printf("%d\n", sizeof(struct S1));
	//练习2
	struct S2
	{
		char c1;
		char c2;
		int i;
	};
	printf("%d\n", sizeof(struct S2));
    return 0;
}

        (留给空间给盆友么思考!!!先思考,再看下面的)        

        盆友们,对于练习1和练习2的的答案是多少呢?

        相信刚接触的铁子们肯定觉得s1和s2的长度都是6吧(32位系统中)。这很简单呀,s1和s2里面都是char char int  三个变量类型,为 1 1 4。所以加起来是6咯。这有啥难的。

        其实,非也。咱们调试起来瞧瞧看。

        是 12 和 8……

        为啥会这样呢?

        12 和 8 这个答案是不是说明了s1和s2里面的数据居然不是挨着存的(如果是挨着的,就是6),显然之间存了空白的东西。

        既然结构体里面存了空白的位置,那么不妨试试看,结构体内两个变量之前到底相隔多少空白位。

        这里用到offsetof函数

        这个函数是用来计算结构体成员相对于结构体起始位置的偏移量。(不理解没关系,下面会有图解。)

        函数的用法可以参考:(30条消息) C语言中 offsetof 的使用_Npgw的博客-CSDN博客_offsetof头文件

        (随便找的,我真贴心!!!)

        话不多说,请看代码  :

int main()
{
	//练习1
	struct S1
	{
		char c1;
		int i;
		char c2;
	};
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", offsetof(struct S1,c1));
	printf("%d\n", offsetof(struct S1, i));
	printf("%d\n", offsetof(struct S1, c2));
	printf("\n");
	//练习2
	struct S2
	{
		char c1;
		char c2;
		int i;
	};
	printf("%d\n", sizeof(struct S2));
	printf("%d\n", offsetof(struct S2, c1));
	printf("%d\n", offsetof(struct S2, c2));
	printf("%d\n", offsetof(struct S2, i));
    return 0;
}

      答案:

 对于s1:

        c1相对与起始位置偏移量是0个字节

        i相对与起始位置的偏移量是4个字节

        c2相对与起始位置的偏移量是8字节

对于s2:

        c1相对与起始位置的偏移量是0个字节

        c2相对与起始位置的偏移量是1个字节

         i相对与起始位置的偏移量是4个字节

图示:(认真看图!!!营养全在图里面)

S1

S2

         那么关于s1和s2怎么存储的,我们就弄清楚了,那么这样存的依据是什么,按照怎样的规则来存结构体呢?

    接着往下看……

1.1结构体对齐规则四步走:

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

        2.其他成员变量要对齐某个数字(对齐数)的整数倍的地址处

注意:

        对齐数是编译器默认的一个对齐数和改结构体成员变量大小的最小值。

        比如,VS的默认对齐数是8个字节 加入结构体中又int ,那么int就要存到偏移量为4的整数倍的地址处。但是注意的是,Linux系统下不设置对齐数,对齐数字就是结构体成员自身的大小。

        3.结构体总大小为最大对齐数(可能每个成员都有自己的对齐数,找个最大的)的整数倍。

        4.特殊情况:

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

为了方便理解,再举个例子:

看代码:

//练习3
int main()
{
	struct S3
	{
		double d;
		char c;
		int i;
	};
	printf("%d\n", sizeof(struct S3));
	//练习4-结构体嵌套问题
	struct S4
	{
		char c1;
		struct S3 s3;
		double d;
	};
	printf("%d\n", sizeof(struct S4));

	return 0;
}

直接用上述规则:

看图(!!!):

s3:

s4:

 详细分析:

        对于第一步,用规则1,直接存。

        第二步,struct s3 s3  里面的最大对数是8,所以存偏移量为8的整数被的的地址处,存16个字节。

        第三步,d的存法如同第二步,也是在8的整数倍开始存(偏移量24地址处)

        第四步,看看结构体整体大小是不是最大对齐数的整数倍,对于练习4刚好是的(32字节),所以不需要补空白位置,假如s4存完之后发现只有30个字节,那么需要在30后面补充两个空白位置,到32(这样才是8的倍数)。

2.为什么要会存在内存对齐

        其实并没有一个官方的说法,但是从大部分的资料上来看,存在内存对齐是出于以下两个考量。

1.平台原因:

        不是所有的硬件平台都能够访问任意地址上的任意数据,某些平台只能在某些地址取出某种特定类型的数据,否则会出现硬件异常。

2.性能原因:

        数据结构(尤其是栈区)应该尽可能的在自然边界上对齐。(感觉咯不懂是吧,没关系,看图)。原因在于,为了访问未对齐的数据,处理器需要两次访问,二对齐的数据只需要一次访问。

        计算机访问数据要么是按照4个字节来访问,要么是按照八个字节来访问,假设某计算机按照四个字节访问数据

        因此,结构体的内存对齐是用空间来换取时间的策略,虽然所占空间大了,但是访问用的时间却短了很多。

        那么怎样设计结构体既能够满足对齐的要求,又满足节省空间呢?

        看看前面的 struct s1 和struct s2 ,明明存的东西一样,但是后者的内存小。

        所以,对于结构体内部成员,相同变量类型的尽量相邻,这样设计的结构体占的空间会相对小一点。

2.1怎么修改默认对齐数

        当然对齐数字,也是能过够修改的

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

3.结构体传参

        结构体传参的两种方式:

        看代码:

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
 printf("%d\n", ps->num);
}
int main()
{
 print1(s);  //传结构体
 print2(&s); //传地址
 return 0;
}

        上面print1和print2函数,哪个更好一点?

        是print2,为什么呢?

        因为函数在传参的时候,参数需要压栈的,会有对时间和空间上的系统开销,如果传递的一个结构体对象的时候,结构体太大,那么参数压栈的系统开销比较大,会导致性能的下降,所以函数传参,尽可能选择传址调用。

4.叠甲

        所有内容都是菜鸟的学习过程记录

        如果有错误的地方,读者轻点喷。

        觉得还行的话,点赞,评论,关注(求求家人们!!!)

        如果有疑问,请在评论区发言,看到的,都会回,爱你们!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C语言】——结构体进阶:结构体的内存对齐(超详细) 的相关文章

  • 【BIM入门实战】Revit安装失败的常见问题及解决办法汇总

    本文总结一下Win7 Win11系统之上 Revit安装失败的常见问题及解决办法 期望能帮到大家 文章目录 1 Revit2018安装完成 某些产品无法安装 2 Revit打开启动时候闪退完美解决 3 软件安装的路径问题 4 电脑系统版本问
  • 【ArcGIS Pro微课1000例】0026:如何制作三维电子沙盘(DEM+DOM)?

    本文讲解ArcGIS Pro3 0中文汉化版中 基于DEM拉伸DOM 实现三维电子沙盘 文章目录 一 创建高程数据源 二 设置高程数据源 一 创建高程数据源 参考阅读 ArcGIS Pro微课1000例 0006 ArcGIS Pro 2
  • 【BIM+GIS】ArcGIS Pro中对Revit的Rvt数据进行地理配准操作

    在ArcGIS Pro中 打开Revit的rvt格式数据 默认是没有坐标系 且位置会放置在原点位置 0 0 在实际使用过程中 需要对rvt数据进行地理配准 包括平移 旋转等操作将bim数据放置在正确的位置 文章目录 一 加载BIM数据 二
  • 【BIM+GIS】ArcGIS Pro在地理数据库中使用BIM内容

    文章目录 一 BIM地理数据库概述 二 BIM文件至地理数据库 三 结束语 一 BIM地理数据库概述 可以在地理数据库中组织 ArcGIS 中的数据作为其地理空间信息的记录系统 可在地理数据库中表示建筑物信息建模 BIM 内容 以利用其诸多
  • 【BIM+GIS】Revit在ArcGIS Pro中的组织方式详解

    ArcGIS Pro 中的 Revit 文件是组织成工作空间 数据集和要素类 因此它们只是类似于地理数据库或 shapefile 的数据源 而且是只读数据源 一 受支持的 Revit 几何 Revit 文件 RVT 包含根据对象分组 称为类
  • 【BIM+GIS】ArcGIS Pro3.0打开多种格式三维模型案例教程

    本文讲解在ArcGIS Pro3 0打开BIM模型 rvt 倾斜模型OSGB Sketchup skp 3d max 3ds 点云数据 las 的方法及注意事项 文章目录 一 ArcGIS Pro打开BIM rvt 二 ArcGIS Pro
  • 【点云概述】什么是点云、来源、种类、特点、处理?

    文章目录 一 什么是点云 二 点云的来源 三 点云的种类 四 点云的特点 五 点云的处理 一 什么是点云 点云 point cloud 是空间中点的数据集 可以表示三维形状或对象 通常由三维扫描仪获取 点云中每个点的位置都由一组笛卡尔坐标
  • 深入剖析Redis客户端Jedis的特性和原理

    一 开篇 Redis作为目前通用的缓存选型 xff0c 因其高性能而倍受欢迎 Redis的2 x版本仅支持单机模式 xff0c 从3 0版本开始引入集群模式 Redis的Java生态的客户端当中包含Jedis Redisson Lettuc
  • 【BIM+GIS】Supermap打开BIM Revit模型的方式

    Revit导出Supermap GIS格式数据的方法通常有三种 插件式导出 直接导入和标准交换格式 IFC 导出 文章目录 一 Revit安装Supermap插件 1 安装Supermap插件 2 UDB导出模型 3 打开模型 二 Revi
  • 【BIM+GIS】Supermap加载实景三维倾斜摄影模型

    OSGB是常见的倾斜模型格式 本文讲述如何在Supermap中加载实景三维倾斜摄影模型OSGB 文章目录 一 生成配置文件 二 加载倾斜模型 1 新建场景 2 添加模型 3 高程调整 一 生成配置文件 点击 三维数据 数据管理 生成配置文件
  • 【BIM+GIS】BIM模型导入GIS软件之前的一些处理设置

    文章目录 一 模型位置发生偏移 二 模型对象丢失或增加 三 模型材质发生变化 四 导出过程缓慢 五 模型属性批量丢失 一 模型位置发生偏移 在视图 可见性 图形替换模型类别 场地 VV可见性快捷 勾选项目基点 单击选中项目基点 在属性中修改
  • vscode中用快捷键 Alt + Shift + F 格式化代码不生效的问题

    vscode中用快捷键 Alt 43 Shift 43 F 格式化代码不生效 xff08 文件中 script脚本部分的代码没有格式化 xff09 的问题 在 vscode 中书写代码 xff0c 有时候缩进格式不一致 xff0c 代码没有
  • 查询 Linux 命令属于哪个软件包

    在 Linux 中 xff0c 有些命令的名称软件包的名称是不一样的 xff0c 或者一个软件包中包含有多个命令 有时候 xff0c 我们需要确定某个命令来自于哪个软件包 xff0c 以便于可以在其他机器上安装 xff0c 或者寻找该软件包
  • 一文读懂 NMEA-0183 协议数据

    NMEA 是 National Marine Electronics Association 的缩写 xff0c 是美国国家海洋电子协会的简称 xff0c 现在是 GPS 导航设备统一的 RTCM 标准协议 NMEA 0183 协议是目前
  • 使用 GPSD 快速读取 GPS 信息

    在 Linux 系统中 xff0c 如果你需要连接 GPS 传感器进行测试 xff0c 那么使用 GPSD 可以满足你的需求 xff0c 帮助你快速实现 GPS 数据的获取 本文将使用导远 INS570D 传感器 xff08 车载高精度组合
  • 如何将 Markdown 格式文章快速发布到微信公众号

    如果你和我一样 xff0c 平时习惯使用 Markdown 写作 xff0c 当我们想把文章发布到微信公众号或知乎等平台时 xff0c 就会发现挺麻烦的 xff0c 尤其是排版方面 xff0c 因为它们并不支持 Markdown 格式 那有
  • 推荐几款好用的数据库管理工具

    本文主要介绍几款常用的数据库管理软件 xff08 客户端 xff09 xff0c 包括开源 免费的 商用收费的 xff0c 其中有一些是专用于 MySQL 数据库的 xff0c 例如 MySQL Workbench phpMyAdmin x
  • Ubuntu 22.04(Jammy)安装 ROS2(Humble)

    本文介绍如何在 Ubuntu 22 04 xff08 Jammy xff09 上安装 ROS 2 软件包 xff0c ROS 2 的版本是当前最新的 Humble Hawksbill 本教程适用于 amd64 和 arm64 平台 准备工作
  • 从落地效果看,转转选择TDengine的三个理由

    在转转的业务中 xff0c 我们使用了Nginx作为我们的反向代理 xff0c 为保证代理层可用性 xff0c 需要对Nginx进行实时状态监控 在服务器的基础监控的选择上 xff0c 我们将OpenFalcon逐步替换为夜莺 xff0c
  • 计算字符串 s 构成的最长回文串长度

    题目 给定一个包含大写字母和小写字母的字符串 s 返回通过这些字母构成的最长的回文串 在构造过程中 请注意区分大小写 比如 Aa 不能当做一个回文字符串 示例 1 输入 s abccccdd 输出 7 解释 我们可以构造的最长的回文串是 d

随机推荐

  • 常见激光雷达厂商 SDK 软件包汇总

    本文收集目前主要激光雷达厂商的 SDK 软件包 xff0c 其中大多还会提供 ROS xff08 ROS1 和 或 ROS2 xff09 驱动软件包 使用这些 SDK 软件包 xff0c 可以快速地将激光雷达集成到你的智能系统中 xff0c
  • GitHub 开启 2FA 双重身份验证的方法

    为什么要开启 2FA 自2023年3月13日起 xff0c 我们登录 GitHub 都会看到一个要求 Enable 2FA 的重要提示 xff0c 具体如下 xff1a GitHub users are now required to en
  • 在 Linux 系统中如何查看和指定 C 语言标准

    本文主要介绍在 Linux 系统中如何查看当前支持的 C 语言版本 xff0c 以及在编译时如何指定 C 语言标准 目前常见的 C 语言标准有 C89 C99 C11 和 C17 xff0c 详情可参考 C语言标准 查看 C 语言标准 我们
  • 使用Openssl EVP函数计算Hash值

    OpenSSL EVP函数库提供了一组用于加密操作高层接口 xff0c 其中也包含了计算消息摘要Hash值得函数 OpenSSL提供了MD2 MD4 MD5 sha1 sha256 sha512等多种Hash算法 计算Hash值相关API
  • 51单片机+L298N控制步进电机【T型】加减速

    本文介绍另一种常见的步进电机 T型 加减速方法 关于L298N模块的使用介绍 以及S型加减速可以上一篇文章 xff0c 传送门如下 xff1a 51单片机 43 L298N控制步进电机S曲线加减速 1 与S曲线加减速对比 1 S曲线一般用查
  • curl用法2

    CURLOPT FTPSSLAUTH The FTP authentication method when is activated CURLFTPAUTH SSL try SSL first CURLFTPAUTH TLS try TLS
  • 简单小实验——串口控制LED灯(HAL库)

    硬件设备 STM32F103ZET3开发板 STLink 功能实现 发送ON LED亮 发送OFF LED灭 其他指令串口发送error 实验过程 波特率设置为115200 数据位8 奇偶校验位None 停止位1 这里我采用的是中断的方式去
  • sudo apt-get update报错【错误:5 http://packages.ros.org/ros/ubuntu xenial InRelease】

    sudo apt get update报错 错误 5 http packages ros org ros ubuntu xenial InRelease 解决办法 xff1a ros的wiki官网 xff0c 更新公钥 网址http wik
  • windowns10下安装MySQL

    安装时参考一下这个链接 xff0c 亲测可用 https blog csdn net qq 45173404 article details 107369405 fps 61 1 amp locationNum 61 2
  • Semi-join在Greenplum中的三种实现方式

    Semi join xff08 半连接 xff09 是用来处理外表的记录是否在内表中存在与其匹配的行 xff0c 而无需考虑匹配行的条数 xff0c 半连接的返回结果集仅使用外表数据集 xff0c 使用场景如 xff1a in exists
  • C#在不同VS版本切换时报错未能找到类型或命名空间名称MySQL

    C 未能找到类型或命名空间名称MySQL 在尝试各种方法无效后 xff0c 通过下方链接第三种情况解决问题 https blog csdn net li gege article details 103416210 utm medium 6
  • Lipschitz 条件或者Lipschitz连续

    https blog csdn net tanga cc article details 86362897
  • STM32F4开发板学习笔记一

    STM32F4是由ST xff08 意法半导体 xff09 开发的一种高性能微控制器 xff0c 之前学习过51单片机的相关知识 xff0c 现在看STM32F4的芯片 xff0c 不得不说它强大的外设功能 xff0c 还有就是固件库 对于
  • 类的三种继承方式

    类的继承方式 公有型 xff08 public xff09 保护型 xff08 protected xff09 私有型 private xff0c 访问控制修饰符也有public protected private3种类型 继承方式不同 x
  • C++ 中在函数的前面加上static的作用

    在一般的函数前面加上static 作用是 xff1a 加了static后表示该函数失去了全局可见性 xff0c 只在该函数所在的文件作用域内可见 当函数声明为static以后 编译器在该目标编译单元内只含有该函数的入口地址 没有函数名 其它
  • 网络字节序跟主机字节序有什么区别

    网络字节序跟主机字节序有什么区别 xff0c 这是我16年9月份校招面试腾讯被问到的一个问题 xff0c 也是Linux服务器开发岗常考的面试题 字节序分为大端字节序和小端字节序 大端字节序是指一个整数的高位字节 xff08 32 31bi
  • 不使用strcat函数,实现两个字符串连接

    设计思路 xff1a 1 首先我们要找到目标字符串的结尾 xff0c 即 0 处 2 然后我们再依次把源字符串依次添加到目标字符串的结尾即可 实现源码如下 xff1a span class token macro property span
  • 使用环形缓冲区ringbuffer实现串口数据接收

    文章目录 1 ringbuffer简单介绍2 ringbuffer的代码实现2 1 ringbuffer数据结构定义2 2 ringbuffer初始化2 3 ringbuffer写数据2 4 ringbuffer读数据 3 在串口中使用ri
  • 结构体(struct)

    什么是结构体 结构是程序员定义数据类型 xff0c 与类非常相似 它们有数据成员和函数成员 xff0c 虽然相似 xff0c 但是有许多区别 xff0c 区别如下 xff1a 类是引用类型二结构是值类型 结构是隐式密封的 xff0c 这意味
  • 【C语言】——结构体进阶:结构体的内存对齐(超详细)

    前言 xff1a 上一篇已经讲了结构体的基本用法 相信各位小伙伴以经学会怎么使用 但是还有一个问题没有弄明白 结构体到底多大 xff0c 占内存空间多不多 xff0c 以经系统到底怎么访问结构体内的数据的 接下来 xff0c 详细分析一下结