【C语言】结构体内存对齐:热门面试话题

请添加图片描述
🔥引言

书接上文,我们了解关于结构体的基本知识,这篇将深入剖析结构体中一个重要的知识点:内存对齐
关于内存对齐是属于热门面试话题,对此单独放在一篇来分享

请添加图片描述

Alt

🌈个人主页:是店小二呀
🌈C语言笔记专栏:C语言笔记
🌈C++笔记专栏: C++笔记

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅
请添加图片描述

文章目录

  • 一、结构体中内存对齐
    • 1.1 对齐规则
    • 1.2 内存对齐的意义
    • 1.3 #pragma(预处理指令)
      • 1.3.1 pragma相关介绍
      • 1.3.2 #pragma pack(n)修改默认对齐数
  • 二、结构体实现位段
    • 2.1 位段的概念
    • 2.2 位段的内存分配
    • 2.3 位段的跨平台问题
    • 2.4 位段的应用


一、结构体中内存对齐

1.1 对齐规则

  • 结构体第一个成员变量对齐相对于结构体成员地址偏移量为0的位置上

  • 其他成员变量需要对齐到对齐数的整数倍

  • 结构体总大小最大对齐数的正数倍

如果存在嵌套结构体的情况,嵌套结构体占用空间需要对齐自身最大对齐数的整数倍,同时在计算结构体总大小的时候,嵌套结构体的最大对齐数参与比较

注意】:对齐数 == 编译器默认的一个对齐数与该成员变量大小的较小值

  • 在vs环境下,系统默认对齐为8

  • 在Linux中没有默认对齐数,对齐数就是成员自身的大小


通过题目熟练的掌握以上知识.

struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));--12struct S2
{char c1;char c2;int i;
};
printf("%d\n", sizeof(struct S2));--8struct S4
{char c1;struct S2 s2;double d;
};
printf("%d\n", sizeof(struct S2));--24

在这里插入图片描述
在这里插入图片描述

说明】:数值代表的是结构体变量地址处的偏移量


1.2 内存对齐的意义

⼤部分的参考资料都是这样说的

平台原因(移植原因)

  • 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常

性能原因

  • 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;对齐的内存访问仅需要⼀次访问。

假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

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

!(https://img-blog.csdnimg.cn/direct/db9b7a7f327947f7a81d64a1a2254b68.gif)

通过上述的观察,不难看出。如果不存在内存对齐,需要执行两个内存访问(对象被分放在两块内存块),而内存对齐只需要进行一次。

对此在涉及结构体时,需要考虑满足对齐,又要节省空间。可以将占用空间小的成员尽量集中在一起

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i
};
S2 < S1

1.3 #pragma(预处理指令)

1.3.1 pragma相关介绍

  • 用于指定计算机或操作系统特定的编译器功能
  • 根据定义pragma指令是计算机或操作系统特定的,并且通常对于每个编译器而言都有所不同
  • pragma指令可用于条件语句以提供新的预处理器功能,或为编译器提供实现所定义的信息,

1.3.2 #pragma pack(n)修改默认对齐数

#include <stdio.h>
#pragma pack(1)//设置默认对齐数为1
struct S
{char c1;int i;char c2;
};
#pragma pacK()//取消默认对齐数,还原为默认对齐数
int main()
{printf("%d\n",sizeof(struct S));return 0;
}

推荐使用场景,在结构体进行内存对齐时,如果对于对齐方式不能达到预期,可以通过该指令更改默认对齐数

获得该成员变量的偏移量

这里需要使用一个函数offsetof()宏,该函数被声明在stddef.h文件中,以下是函数offsetof()宏

size_t offsetof(type,member);

宏定义】:

#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)

如果想要了解更多,可以参考下这篇博客Offsetof宏详解-CSDN博客.这里只如何去使用Offsetof()宏计算出结构体某成员地址的偏移量。

#include <stdio.h>
#include <stddef.h>
struct S
{char a;int i;
};
int main()
{printf("%d\n",offsetof(struct S,i));//那么这里的结果就是就是4return 0;
}

小总结】:
结构体中的内存对齐是为了以空间换取时间的做法,随着计算机不断地更新换代,一般不需要担心内存空间不足的问题,逐渐地从更多考虑的是时间上的问题。同时为了节约空间的开销,提出位段


二、结构体实现位段

2.1 位段的概念

位段是结构体的一种变形,在功能、用法上与结构体基本一致,但是在于内存分配上不同,位段可以很好的节省空间,可存在位段跨平台的问题。同时与结构体相比有两个点不同。

  • 成员上:intunsigned intsigned int,但是在C99中是可以选择其他类型
  • 格式上:位段成员名后面有一个冒号和一个数字
struct A
{char _a:2;char _b:5;
};

【说明】:这里数字代表的是该成员变量占用空间大小,而大小单位是比特

【问题】:位段A所占的内存大小是多大?

这个问题,需要利用下面的知识了


2.2 位段的内存分配

  • 位段成员:intunsigned intsigned int或者char等类型(需要是整形,是要转换为二进制
  • 位段开辟空间的大小一般是以四个字节或一个字节开辟的
  • 位段涉及许多不确定的因素,位段是不跨平台的,注意可移植的程序,应该避免使用位段
struct S
{char a:3;char b:4;char c:5;char d:4;
};struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

2.3 位段的跨平台问题

不确定的因素大致包括】:

  1. 内存存放的方向是从左到右,还是从右到左
  2. 是低地址到高地址,还是高地址到低地址
  3. int类型是不确定是被当作有符号数还是无符号数
  4. 当一个结构体包括了两个位段,第二个位段比较大,无法容纳第一个位段剩下的空间,是舍弃还是利用剩下的空间,这是不确定的
  5. 位段中最大位的数目不能确定(16位机器最大16,32位机器最⼤32,写成27,在16位机器会出问题),可能会冲出最大的范围,出现问题

我们不妨以vs2013环境下测量下数据

vs2013下,位段是从左到右,从低地址到高地址,位段需要的空间不足,直接开辟一块新的空间,我们来结合图片理解下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【步骤】:

  1. 位段开辟八个bit位(这里是char类型的情况)
  2. 位段成员后面数字是占用多少bit位
  3. 根据变量数据,转化为二级制(一个二级制为一个比特位),根据位段对应的数据,将转为的二级制多个比特位放入
  4. 关于上不确定因素中(4),vs2013选择舍弃,那就开辟一块新的空间,重复(1,2,3)步骤

2.4 位段的应用

比如下图中网络协议中,在一个结构存在很多只需要几个bit位就能实现的效果,这里使用位段就能达到想要的效果,也能节省空间的浪费。同时网络传输的数据大小也会小一点,提高了网络的流畅和效率!

位段使用注意事项】:

struct A
{int _a:2;int _b:5;
};
int main()
{//错误的做法struct A s={0};scanf("%d",&s._a);//正确的示范int b=0;scanf("%d",&b);s._b=b;return 0;
}

说明】:

位段的几个成员共有同一个字节,而有些成员的起始位置并不是某个字节的起始位置。对此这些位置是没有地址(内存中每个字节分配一个地址,一个字节内部的bit位是没有地址的

解决办法】:

可以将值放入一个变量中,再通过赋值给位段成员,这个赋值在以后的操作中,是很巧妙的用法的。


在这里插入图片描述
以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二C语言笔记,希望对你在学习C语言中有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14007.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3D工业视觉

前言 本文主要介绍3D视觉技术、工业领域的应用、市场格局等&#xff0c;主要技术包括激光三角测量、结构光、ToF、立体视觉。 一、核心内容 3D视觉技术满足工业领域更高精度、更高速度、更柔性化的需求&#xff0c;扩大工业自动化的场景。 2D视觉技术基于物体平面轮廓&#…

软件无线电学习-第二代移动通信系统过程理解

本文知识内容摘自《软件无线电原理和应用》 无线通信领域让大家感受最深的是民用移动通信的快速发展。民用移动通信在短短的二十年时间里已发展了三代&#xff1a;20世纪80年代的模拟体制(TACS/AMPS)为第一代移动通信(简称1G)&#xff1b;20世纪90年代的数字体制(GSMCDMATDMA)…

Git提交和配置命令

一、提交代码到仓库 在软件开发中&#xff0c;版本控制是一个至关重要的环节。而Git作为目前最流行的版本控制系统之一&#xff0c;为我们提供了便捷高效的代码管理和协作工具。在日常开发中&#xff0c;我们经常需要将本地代码提交到远程仓库&#xff0c;以便于团队协作和版本…

Java基础教程 - 9 集合

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 9 集合 什么是集合&…

【stm32/CubeMX、HAL库】嵌入式实验六:定时器(2)|PWM输出

参考&#xff1a; 【【正点原子】手把手教你学STM32CubeIDE开发】 https://www.bilibili.com/video/BV1Wp42127Cx/?p13&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c3989f6189fd3 《嵌入式系统基础与实践》刘黎明等编著&#xff0c;第九章定时器&#xff0c…

爱普生TG5032SFN温补晶振在机器人控制中的应用

机器人控制是机器人技术的核心组成部分&#xff0c;它涉及通过传感器采集外部环境信息&#xff0c;然后经过信号处理、运动规划和执行控制等步骤&#xff0c;最终实现机器人的运动控制和任务执行。在技术的不断更选&#xff0c;机器人控制也在不断进步和演变。智能化机器人具备…

Z缓冲技术在AI去衣中的关键角色

引言&#xff1a; 人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;为图像处理领域带来了革命性的变化。其中&#xff0c;AI去衣技术作为一种新兴的应用&#xff0c;引起了广泛关注。它不仅在多媒体内容的编辑、虚拟现实和增强现实等领域具有重要的应用价值&…

Jenkins 构建 Maven 项目:项目和服务器在一起的情况

bash.sh内容 #!/bin/bash#删除历史数据 rm -rf ruoyi-admin.jar# appname$1 appnamevideo.xxxxx.com #获取传入的参数 echo "arg:$appname"#获取正在运行的jar包pid # pidps -ef | grep $1 | grep java -jar | awk {printf $2} pidps -ef | grep $appname | grep ja…

1673. 找出最具竞争力的子序列

题目 给定一个整数数组 nums 和一个正整数 k&#xff0c;返回长度为 k 且最具竞争力的 nums 子序列。 数组的子序列是从数组中删除一些元素&#xff08;可能不删除元素&#xff09;得到的序列。 在子序列 a 和子序列 b 第一个不相同的位置上&#xff0c;如果 a 中的数字小于…

mysql 删除特殊字符 表中存了特殊字符 换行符 回车符 word字符 查询不到

省流&#xff1a; UPDATE t1 SET f1 REPLACE(REPLACE( f1 , CHAR(10), ), CHAR(13), ); 用 replace() 函数将 换行符char(10) 和 回车符char(13) 替换为空字符串。 char(10)&#xff1a;换行 char(13)&#xff1a;回车 发现表里存进很多换行符&#xff0c;如下图&#xff1a…

Python中文分词工具库之jieba使用详解

概要 在自然语言处理(NLP)领域,中文文本的分词是一个重要且基础的任务。Python的jieba库是一个广泛使用的中文分词工具,提供了丰富的功能,包括精准模式、全模式、搜索引擎模式等,适用于不同的应用场景。本文将详细介绍jieba库,包括其安装方法、主要特性、基本和高级功能…

java技术:oauth2协议

目录 一、黑马程序员Java进阶教程快速入门Spring Security OAuth2.0认证授权详解 1、oauth服务 WebSecurityConfig TokenConfig AuthorizationServer 改写密码校验逻辑实现类 2、oauth2支持的四种方式&#xff1a; 3、oauth2授权 ResouceServerConfig TokenConfig 4、…

MySQL的索引是什么

MySQL的索引 一、索引概述二、索引结构1.简要概述2.从二叉树说起3.再在说下B-Tree4.为什么选择BTree5.Hash又是什么6.博主被面试官经常问的题目 三、索引分类四、聚集索引&二级索引五、索引语法 一、索引概述 1.索引是帮助MySQL 高效获取数据的数据结构(有序)。在数据之外…

[STM32-HAL库]Flash库-HAL库-复杂数据读写-STM32CUBEMX开发-HAL库开发系列-主控STM32F103C6T6

目录 一、前言 二、实现步骤 1.STM32CUBEMX配置 2.导入Flash库 3.分析地址范围 4.找到可用的地址 5.写入读取普通数据 6.写入读取字符串 6.1 存储相关信息 6.2 存取多个参数 三、总结及源码 一、前言 在面对需要持久化存储的数据时&#xff0c;除了挂载TF卡&#xff0c;我们…

燃数科技前端25-40K*14薪一面超简单,下周二面啦

一面 1、自我介绍 2、低代码如何设计的 3、react路由原理 4、react生命周期 5、什么是回调地狱&#xff0c;如何解决 6、jwt和session有什么区别 7、js文件相互引用有什么问题&#xff1f;如何解决 8、一个很大的json文件&#xff0c;前端读取如何优化 面试我的不像是…

为什么说 Redis 是单线程的?——Java全栈知识(25)

为什么说 Redis 是单线程的&#xff1f; 我们常说的 Redis 是单线程的&#xff0c;但是我前面在讲持久化机制的时候又说 RDB 的持久化是通过主进程 fork 出一个子进程来实现 RDB 持久化。那么 Redis 到底是多线程还是单线程的呢&#xff1f; Redis 的网络 IO 和键值的读写是单…

数据库|基于T-SQL创建数据库

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; SQL Server用于操作数据库的编程语言为Transaction-SQL,简称T-SQL。 本节学习基于T-SQL创建数据库。以下为学习笔记。 01 打开新建查询 首先连接上数据库&#xff0c;点击【新建查询】打开新建查询窗口&#xff0c; …

C++—结构体

结构体&#xff08;struct&#xff09;&#xff0c;是一种用户自定义复合数据类型&#xff0c;可以包含不同类型的不同成员。 结构体的声明定义和使用的基本语法&#xff1a; // 声明结构体struct 结构体类型 { 成员1类型 成员1名称; ...成员N类型 成员N名称; };除声明…

【计算机视觉(2)】

基于Python的OpenCV基础入门——视频的处理 视频OpenCV视频处理操作&#xff1a;创建视频对象判断视频是否成功初始化读取视频帧获取视频特征设置视频参数声明编码器保存视频释放视频对象 视频处理基本操作的代码实现&#xff1a; 视频 视频是由一系列连续的图像帧组成的。每一…

Spring—IoC

目录 1. IoC的提出 2. Spring容器 2.1. Spring容器实现原理 2.2. Spring组件 2.2.1 XML标签方式 2.2.2. 类注解方式 2.2.3. 方法注解方式 2.3. Spring容器分类 2.3.1. BeanFactory容器 2.3.2. ApplicationContext容器 2.3.3. WebApplicationContext容器 3. Spring中…