结构体内存对齐良心讲解

浅析结构体对齐 浅析结构体对齐,用平白易懂的话做分享 阅读详情

本文将介绍以下内容:

一、什么是内存对齐?
二、结构体大小如何计算?
三、为什么存在内存对齐?
四、如何修改默认对齐数?
五、笔试题练习

正文

一、什么是内存对齐?

  首先,内存对齐是属于编译器的“管辖”范围,编译器为程序中的每个数据单元安排在适当的位置上。但是在 c 语言中我们是被允许干预内存对齐的,也就是说我们可以一定程度上的控制如何内存对齐。
内存对齐是为了硬件计算起来效率更高而设计的
  我们通过一些例子来理解下什么是内存对齐:(以下程序皆在vs2013下运行)
例1:

typedef struct s1
{
	char a;
	int b;
	char c;
} s1;
printf("%d\n", sizeof(s1));

运行结果是12。

typedef struct s1
{
	char a;
	char b;
	char c;
} s1;
printf("%d\n", sizeof(s1));

运行结果是3。

typedef struct s1
{
	char a;
	short b;
	char c;
} s1;
printf("%d\n", sizeof(s1));

运行结果是6。

typedef struct s1
{
	char a;
	double b;
	char c;
} s1;
printf("%d\n", sizeof(s1));

运行结果是24。


从例1直观的来看,感觉好像是低字节的元素在向高字节对齐。我们继续往下看!!!


例2:

typedef struct s2
{
	char a;
	char b;
	int c;
} s2;
printf("%d\n", sizeof(s2));

运行结果是8。

例3:

typedef struct s3
{
	double d;
	char c;
	int i;
} s3;
printf("%d\n", sizeof(s3));

运行结果是16。


例 2 应该是将两个 char 凑成一个4字节的 int,所以计算出来是8。
例 3 应该是将 char 和 int 凑成了一个 8字节的double,所以计算出来是16。
这看起来也是往低字节往高字节的元素对齐。那么看下一个例子!!


例4:

	typedef struct s3
	{
		double d;
		char c;
		int i;
	} s3;
	
	typedef struct s4
	{
		char c1;
		struct s3 s3;
		double d;
	} s4;
	printf("%d\n", sizeof(s4));

运行结果是32。


注意:这里结果发生了变化,如果按照我们上面的推论,那么这个计算出来应该是往 s3 这个结构体上对齐,s3 是16个字节,那么按理应该是 48 才对,看起来这并不是无休止的往最大的字节元素对齐。那么我们就来看看结构体的大小到底应该如何计算?


二、结构体大小如何计算?

  我们先来看看结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移为 0 的地址处。
  2. 其他成员变量要对齐到对齐数的整数倍
      对齐数 = 编译器默认的一个对齐数 与 该成员中最大的变量 两者之间较小的那个
      提一下:vs中默认的对齐数是 8
          Linux下默认的对齐数是 4
  3. 结构体总大小为最大对齐数(每一个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

看了这些规则,我们来一条条分析:
  第一条很好理解,简单来说,结构体第一个成员的地址和结构体的地址是一样的。但是意义不一样,第一个成员的地址是给结构体的地址加上一个 0 得到的就是第一个成员的地址。
  主要是第二条看起来比较绕口,但是仔细分析也是很好理解,这时候可以看我们的例子,在例2中,结构体中最大的成员是 int ,而vs默认的对齐数是8,我们选两者中较小的一个作为对齐数,那么就是取 4 作为对齐数。
  再看例 3 中,结构体成员最大的是 double 有 8 个字节,而vs的默认对齐数也是 8 ,所以取 8 作为对齐数。
  再看例 4 中,结构体成员中最大的是结构体 s3 ,是 16 个字节,而vs默认对齐数是8,取两者较小的作为对齐数,所以就取的是 8 作为对齐数,s3 的大小还是 16 个字节,其他成员变量向 8 对齐。

三、为什么存在内存对齐?

  大部分的参考资料都是这样解释的:
1、平台原因(移值原因):
  不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取特定类型的数据,否则抛出硬件异常。
2、性能原因:
  数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
  原因在于,为了访问未对齐的内存,处理器需要作出两次内存访问,而对齐的内存仅需要一次访问,也就是下面的解释。
  cpu在取数据的时候一般都是 4 个字节或 8 个字节的取,如果没有内存对齐这回事那么取数据就会出现下面的情况
在这里插入图片描述
没有内存对齐的话,取出来的数据就是一个 char 和 3/4 个 int,不是一个完整的 int,为了取出一个完整的 int 还得再取一次,这样就很僵硬了。
  但是当我们把 char 补成四个字节,那么每次就可以取出来完整的数据了,cpu 再去解析也就方便了很多。所以说内存对齐是为了硬件计算起来效率更高设计的。
在这里插入图片描述

总的来说,结构体的内存对齐是用 空间 换 时间 的做法
四、如何修改默认对齐数?

  借助预处理指令 #pragma pack()
#pragma pack(2) :将默认对齐数修改为2。默认对齐数一旦修改,那么结构体计算的大小也就发生了相应的变化。
#pragma pack() :取消设置的默认对齐数,还原为默认对齐数。

五、笔试题练习

提问:写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明?
答:

typedef struct s1
{
	char a;
	int b;
	char c;
} s1;

#define OFFSET(a, b) ((char*)(&b) - (char*)(&a))

int main()
{
	s1 s;
	printf("成员 b 相对于首地址的偏移量为:%d\n", OFFSET(s, s.b));
	system("pause");
	return 0;
}

如果直接用两个指针进行相减,减出来肯定有问题,因为这里的指针是带类型的,所以我们需要将指针强转为char*进行相减,那么计算出来的就是字节数。
在这里插入图片描述


以上就是内存对齐的相关知识!!!
视频教程-Swift5语言入门实例教程-Swift Swift5语言入门实例教程 10年以上IT行业工作经验、三年以上IT行业教... 阅读详情

相关推荐

C-Lang手写HashMap,感受散列表的魅力

1  哈希Map   今天要聊的,是一个在Java/Android面试中被问烂了的类: Hashmap. 这个类如此的被看重,上至阿里 高P面试,下到 数据结构入门教材。   甚至刚上门取件的快递小哥都能跟你用背课文的语气侃侃而谈,hashmap的底层是数组加链表。   但是今天要聊的又有点不太一样。你真的知道啥是hashmap,为啥要数组加链表吗?   2    散列表  ...

学海有涯 574

结构体对齐

结构体对齐规则: 1.结构体变量的首地址一定 是这个结构体变量中 最大的基础(内置)类型的大小的整数倍 2.结构体变量中每一个成员 相对于结构体首地址的偏移 一定是该成员的基础数据类型大小的整数倍 3.结构体变量的总大小 一定是这个结构体变量中 最大的基础类型的大小的整数倍 示例: 如果允许修改默认对齐大小,则规则变更: 默认的内存对齐大小 :vs默认8字节 gcc默认4字节 1.结构体变量的首地址一定 是这个结构体变量中 Min(最大的基础(内置)类型的大小, 默认的对齐规...

小程的博客 523

国人自制C语言图书管理系统教程

本文还有配套的精品资源,点击获取 简介:《国人原创良心自制图书管理系统C语言版》是一个用C语言开发的实用图书管理软件。C语言以其高效、灵活和可移植性被广泛应用于系统级和嵌入式开发,该系统实现了图书的存储、查询、添加、删除和修改等管理功能。系统中可能用到了结构体来定义图书信息,利用文件系统或自定义数据结构来模拟数据库操作,以及线性搜索、二分查找、冒泡排序、快速排序或归并排序等...

weixin_29840475的博客 1520

21-C语言的结构体尺寸——地址对齐问题

21-C语言的结构体尺寸——地址对齐问题

everything is possible,anything is OK. 1019

野火STM32F407(骄阳)+无刷直流驱动板+永磁同步电机控制学习记录(四)(基于HAL库)

以上内容都是参考别人资料自学的,很多地方都是凭感觉,因为理论部分不太会算,只要电机能转就行,其他性能指标都没考虑,因为不会,所以这纯属是学习记录帖。带宽在500时,电流波动小,但是响应很慢,就是堵转后不太容易恢复转动。速度环带宽设定小了,响应不够快,带宽设定大了波动比较大,电机感觉起来也震一点,而且,当带宽设到200的时候,启动直接就锁死了,应该是PI增益设定太大了。电流环带宽的试验:去掉速度环,让Iq_Set=2,Ud=0,此时电机是电流环闭环控制,用手堵转电机然后松开,看电流的波动以及响应情况。

XUEXIDEXIAOhai的博客 4553

2024年上市公司ESG评级全解析:六大权威数据源横向对比与应用指南

本文深度解析2024年国内主流ESG评级数据源,包括华证、商道融绿、Wind、富时罗素和彭博,横向对比其覆盖范围、评分逻辑与应用侧重点。文章提供实战指南,帮助投资者与企业根据投资决策、风险管理和学术研究等不同场景,精准选择并有效应用ESG数据,实现从概念到实用分析工具的转变。

weixin_29229055的博客 312

结构体对齐规则

结构体对齐规则: 1、第一个成员在于结构体变量偏移量为0的地址处。 2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的 较小值。 3、结构体总大小为最大对齐数(每个成员变量都有自己的对齐数)的整数倍。 4、针对嵌套结构体,嵌套的结构体要对齐到自己最大对齐数的整数倍处,结构体总体大小是所有对齐数的最大值(包含嵌套结构体的对齐数)的整数倍。 下面,来看一个考察结构体对齐方式的面试题: 1、已知结构体成员d的地址为p1,请获取成员变量b的地

FLY__Kun的博客 3400

核密度估计及其Python实践

一、参数估计简介 很多情况下,我们只有有限的样本集,而类条件概率密度函数p(x|ωi)和先验概率P(ωi)是未知的,需要根据已有样本进行参数估计,然后将估计值当作真实值来使用。 由给定样本集求解随机变量的分布密度函数问题是统计机器学习和概率统计学的基本问题之一。解决该问题的方法包括参数估计和非参数估计两大类: 1.参数估计方法 已知概率密度函数的形式而函数的有关参数未知,通过估计参数来估计概率密度函数的方法。有两种主要参数估计法:确定性参数估计方法是把参数看作确定而未知的, 典型方法为最大似然估计。随机参数

袁易学的博客(Yuan Yixue's Blog) 1万+

好用的Splay模板讲解说明

写在前面 #include<cstdio> #include<iostream> using namespace std; const int INF = 0x3f3f3f3f; inline void read(int &w){ w = 0; char c = 0; int f = 1; while(!isdigit(c)){if(c == '-') f = ...

Vanadianfan的博客 283

STM32CubeMX实战:TIM定时器中断配置避坑指南(附100ms精准定时代码)

本文深入解析STM32CubeMX配置TIM定时器中断的常见误区与避坑技巧,重点阐述时钟源、预分频器与自动重装载值的精准计算方法。通过实战案例,提供一份可直接复用的100ms精准定时中断代码模板,并分享调试验证方法,帮助开发者构建稳定可靠的嵌入式系统心跳。

grafana8visual的博客 500

从‘全步’到‘微步’:一张图看懂步进电机细分控制,选对模式让你的项目更丝滑

本文深入解析步进电机细分控制技术,从全步到微步的演进过程,帮助工程师根据项目需求选择最优控制模式。通过对比不同细分等级的性能特点和应用场景,提供3D打印、实验室自动化等领域的黄金配置方案,实现更精准、平稳的电机控制效果。

weixin_29290259的博客 471

指针的这些知识你知道吗?C语言超硬核指针进阶版3w+字详解+指针笔试题画图+文字详细讲解

指针的进阶 文章目录指针的进阶字符指针指针数组数组指针数组指针的使用传统的方式访问二维数组利用数组指针来访问二维数组数组参数、指针参数一维数组传参二维数组传参一级指针传参二级指针传参函数指针—指向函数的指针函数指针函数调用C陷阱和缺陷中的两个代码函数指针数组实现一个计算器普通实现函数指针数组实现指向函数指针数组的指针回调函数qsort函数qsort函数对int型数组排序qsort对结构体排序模拟实现qsort函数指针和数组笔试题一维数组的sizeof和&的那些事字符数组与sizeof、&、s

小赵小赵福星高照的博客 5809

VMware报错系列:虚拟机配置文件(.vmx)损坏或丢失---修复

虚拟机配置文件(.vmx)损坏或丢失---修复

weixin_54626591的博客 7011

C语言(自定义)函数

咦?这都有函数看哦,吼吼吼

dyudbegdu的博客 1986

因果强化学习:优化决策策略的因果方法

1. 背景介绍 1.1 传统强化学习的局限性 强化学习(Reinforcement Learning, RL)是一种通过与环境交互来学习最优决策策略的方法。然而,传统强化学习方法在面对复杂、动态和不确定的现实世界问题时,往往存在以下局限性: 数据效率低:传统强化学习方法

AI天才研究院 657

Linux故障排查和性能优化面试题及参考答案

在 Linux 系统中,负载(Load Average)是衡量系统繁忙程度的一个重要指标,它表示系统在特定时间间隔内运行队列中的平均进程数。负载值反映了系统当前的工作压力,主要包括 CPU 等待运行的进程数量和处于不可中断睡眠状态(通常是等待 I/O 操作完成)的进程数量。负载平均值通常有三个数值,分别代表过去 1 分钟、5 分钟和 15 分钟内的系统平均负载。例如,在使用uptime命令或查看文件时,会看到类似这样的输出,这三个数值依次表示过去 1 分钟、5 分钟和 15 分钟的负载平均值。

大模型大数据攻城狮的专栏 1081

自定义类型详解:位段,枚举,联合(共用体),还有柔性数组!你能找到这么详细的吗?!

每字每句绝对真情实感!看完包懂位段、枚举、联合!不信就进来瞧上一瞧!

月亮永悬不落,剑指巅峰 1149

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

vscode第二讲:内部调试和设置

首先,咱们上次已经了解过它了,这次再来了解一下(因为上次是局部截屏)好的,这是一个完整的局面,观察……左边边栏是上次讲到的内容,从上至下依次是:1.vscode内置资源管理器2.搜索3.git模块(目前不存在,毕竟你又不下载……)4.调试和运行模块5.拓展模块。

llwf_rztk的博客 985

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

为什么越来越多的公司都转型 Go 语言开发?

开局第一问:图片中的三个人是谁,他们做了什么事? 这三位大佬,是计算机领域的三位宗师级大牛 Rob Pike、Ken Thompson 和 Robert Griesemer,也是...

程序员闪充宝 2217

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

EasyRTMP实现的rtmp推流的基本协议流程

EasyRTMP介绍EasyRTMP是结合了多种音视频缓存及网络技术的一个rtmp直播推流端,包括:圆形缓冲区(circular buffer)、智能丢帧、自动重连、rtmp协议等等多种技术,能够非常有效地适应各种平台(Windows、Linux、ARM、Android、iOS),各种网络环境(有线、wifi、4G),以及各种情况下的直播恢复(服务器重启、网络重启、硬件设备重启),今天我们讲解的是E

Babosa的专栏 8788

数据结构学习笔记1(时间复杂度、线性表)

本文整理了数据结构基础知识和线性表相关内容。第一部分介绍了时间复杂度计算方法(如O(n²))、空间复杂度概念和抽象数据类型(ADT)的C语言实现。第二部分详细讲解了线性表的两种实现方式:顺序表(数组实现)和链表(非连续内存存储)。以图书管理系统为例,展示了链表的实际应用,包括初始化、头插法、尾插法、指定位置插入、删除节点等核心操作的代码实现,并分析了顺序表和链表在查找、增删操作上的性能差异。

2503_92688805的博客 805

ubuntu 安装clang_LLVM从安装到手写一个入门Pass

今天来总结一下llvm的安装和怎么写一个入门级的自定义pass。 安装(环境:ubuntu18.04。个人尝试的经验:18.10是不行滴,里面用到的libc6库版本不对,会导致build-essential无法正常进行,所以老老实实用支持最广的18.04吧,没必要自己给自己找麻烦)0.安...

weixin_39609051的博客 444

Go 为什么是备受推崇高效率工业化语言?

开局第一问:图片中的三个人是谁,他们做了什么事? 这三位大佬,是计算机领域的三位宗师级大牛 Rob Pike、Ken Thompson 和 Robert Griesemer,也是...

aoho求索 665

Go 语言究竟好在哪里?

开局第一问:图片中的三个人是谁,他们做了什么事? 这三位大佬,是计算机领域的三位宗师级大牛 Rob Pike、Ken Thompson 和 Robert Griesemer,也是...

EDDYCJY的博客 524
上一篇: 设计一个只能在栈上创建对象的类
下一篇: c++万年历
coolnut12138
博客等级 码龄9年 51粉丝 75原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值