结构体对齐(内存对齐)

2023-11-19

本文转自:http://www.ksarea.com/articles/20071004_sizeof-struct-memory.html

 

有的时候,在脑海中停顿了很久的“显而易见”的东西,其实根本上就是错误的。就拿下面的问题来看:

struct T
{
char ch ;
int i ;
} ;

使用sizeof(T),将得到什么样的答案呢?要是以前,想都不用想,在32位机中,int是4个字节,char是1个字节,所以T一共是5个字节。实践出真知,在VC6中测试了下,答案确实8个字节。哎,反正受伤的总是我,我已经有点麻木了,还是老老实实的接受吧!为什么答案和自己想象的有出入呢?这里将引入内存对齐这个概念。

许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。某些处理器在数据不满足对齐要求的情况下可能会出错,但是Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。不过Intel奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐。

ANSI C标准中并没有规定,相邻声明的变量在内存中一定要相邻。为了程序的高效性,内存对齐问题由编译器自行灵活处理,这样导致相邻的变量之间可能会有一些填充字节。对于基本数据类型(int char),他们占用的内存空间在一个确定硬件系统下有个确定的值,所以,接下来我们只是考虑结构体成员内存分配情况。

Win32平台下的微软C编译器(cl.exe for 80×86)的对齐策略:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
备注:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
备注:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
备注:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。

根据以上准则,在windows下,使用VC编译器,sizeof(T)的大小为8个字节。

而在GNU GCC编译器中,遵循的准则有些区别,对齐模数不是像上面所述的那样,根据最宽的基本数据类型来定。在GCC中,对齐模数的准则是:对齐模数最大只能是4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。而且在上述的三条中,第2条里,offset必须是成员大小的整数倍,如果这个成员大小小于等于4则按照上述准则进行,但是如果大于4了,则结构体每个成员相对于结构体首地址的偏移量(offset)只能按照是4的整数倍来进行判断是否添加填充。
看如下例子:

struct T
{
char ch ;
double d ;
} ;

那么在GCC下,sizeof(T)应该等于12个字节。

如果结构体中含有位域(bit-field),那么VC中准则又要有所更改:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

备注:当两字段类型不一样的时候,对于不压缩方式,例如:

struct N
{
char c : 2 ;
int i : 4 ;
} ;

依然要满足不含位域结构体内存对齐准则第2条,i成员相对于结构体首地址的偏移应该是4的整数倍,所以c成员后要填充3个字节,然后再开辟4个字节的空间作为int型,其中4位用来存放i,所以上面结构体在VC中所占空间为8个字节;而对于采用压缩方式的编译器来说,遵循不含位域结构体内存对齐准则第2条,不同的是,如果填充的3个字节能容纳后面成员的位,则压缩到填充字节中,不能容纳,则要单独开辟空间,所以上面结构体N在GCC或者Dev-C++中所占空间应该是4个字节。

4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
备注:
结构体
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

typedef struct
{
  
char c : 2 ;
  
double i ;
  
int c2 : 4 ;
} N3 ;

在GCC下占据的空间为16字节,在VC下占据的空间应该是24个字节。

ps:

  • 对齐模数的选择只能是根据基本数据类型,所以对于结构体中嵌套结构体,只能考虑其拆分的基本数据类型。而对于对齐准则中的第2条,确是要将整个结构体看成是一个成员,成员大小按照该结构体根据对齐准则判断所得的大小。
  • 类对象在内存中存放的方式和结构体类似,这里就不再说明。需要指出的是,类对象的大小只是包括类中非静态成员变量所占的空间,如果有虚函数,那么再另外增加一个指针所占的空间即可。
  • 1.          内存对齐与编译器设置有关,首先要搞清编译器这个默认值是多少

    2.          如果不想编译器默认的话,可以通过#pragma pack(n)来指定按照n对齐

    3.          每个结构体变量对齐,如果对齐参数n(编译器默认或者通过pragma指定)大于该变量所占字节数(m),那么就按照m对齐,内存偏移后的地址是m的倍数,否则是按照n对齐,内存偏移后的地址是n的倍数。也就是最小化长度规则

    4.          结构体总大小: 对齐后的长度必须是成员中最大的对齐参数的整数倍。最大对齐参数是从第三步得到的。

    5.          补充:如果结构体A中还要结构体B,那么B的对齐方式是选它里面最长的成员的对齐方式

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

结构体对齐(内存对齐) 的相关文章

  • 结构体前向声明编译失败

    我有以下代码 但编译器说 sender wrapper 未定义 即使我向前声明了它 我不能对结构进行前向声明吗 用VS2003编译 struct send wrapper struct IPSend IPSend IPSend const
  • typeof() 表达式内的副作用

    在 GNUC C 中 您可以使用typeof expression 并且使用内部带有副作用的表达式是合法的 例如 您可以使用以下 C 代码 int x 0 typeof x y 在这种情况下 副作用被忽略 并且 x 之后仍然为零 这是有道理
  • 在 Ubuntu 上用 C 项目编译和链接 GTK 3

    我相信这不是重复的问题 在发布此问题之前我已经看过所有问题 答案 我想我这里的情况有所不同 我使用Ubuntu 12 04并下载GTK 2 和 3 我从 GNOME 网站复制了一个简单的 GTK 源代码 但是当我在终端中使用这个命令时 gc
  • “struct X typedef”与“typedef struct X”的含义是什么?

    我在现有代码库中有以下 工作 代码 在 C 和 C 之间共享的包含文件中使用 在 MSVC 2010 和 Windows DDK 上编译 struct X USHORT x typedef X PX And enum MY ENUM enu
  • GCC 和 -Wconversion

    让我们编译以下程序 int main uint16 t data 0 data uint16 t std round 3 14f return 0 with g Wconversion prog cpp 我们会得到warning conve
  • `printf()` 中格式说明符“%qd”的用途是什么?

    我看到格式说明符 qd浏览时github https github com Microsoft clang blob master test Sema format strings c代码 然后我检查了 GCC 编译器 它工作正常 incl
  • 在Python中计算结构体的CRC

    我有以下结构 来自 C 中的 NRPE 守护程序代码 typedef struct packet struct int16 t packet version int16 t packet type uint32 t crc32 value
  • 后缀和前缀运算符作为函数参数 - 为什么会发生这种情况? [复制]

    这个问题在这里已经有答案了 在执行以下简单的 C 程序时 我发现了一个非常有趣的事实 include
  • 如何检查给定调用站点的重载决策集

    如何检查重载解析集 我在多个调用站点中使用了 4 个相互竞争的函数 在一个调用站点中 我期望调用一个函数 但编译器会选择另一个函数 我不知道为什么 这不是微不足道的 为了了解发生了什么 我正在使用enable if disable if打开
  • 无法将 C 代码链接到 lapack / blas:未定义的引用

    我已经尝试了好几个小时了 这让我发疯 我得到的最后一个错误是 demo cblas c text 0x83 undefined reference to clapack sgetrf demo cblas c text 0xa3 undef
  • 'goto *foo' 其中 foo 不是指针。这是什么?

    我正在玩标签作为值 https gcc gnu org onlinedocs gcc Labels as Values html并最终得到这段代码 int foo 0 goto foo 我的 C C 经验告诉我 foo means dere
  • for 循环初始值设定项中的结构

    知道为什么 for 循环初始值设定项中的这个结构表达式在编译时会出现语法错误吗 在这种情况下 指向结构的指针工作正常 但 ofc 我需要如下所示的局部变量 感谢您的建议 type Request struct id int line byt
  • gcc 中 -g 选项的作用是什么

    我看到很多关于 gdb 的教程要求在编译 c 程序时使用 g 选项 我无法理解 g 选项的实际作用 它使编译器将调试信息添加到生成的二进制文件中 此信息允许调试器将代码中的指令与源代码文件和行号相关联 拥有调试符号可以使某些类型的调试 例如
  • 这个反斜杠在这段汇编代码中起什么作用?

    我不确定这些推线有什么区别 修剪下来来自 Linux 的 x86 entry calling h https github com torvalds linux blob 241e39004581475b2802cd63c111fec43b
  • 如何让 C# 设计器编辑我的结构属性?

    我正在使用 C 创建一个具有多个自定义属性的自定义 Windows 窗体控件 这些属性之一是一个带有几个整数字段的简单结构 public struct Test public int A B Test Test Category MyCat
  • C99 中数组的静态大小[重复]

    这个问题在这里已经有答案了 一个非常简单的 C 程序 include
  • 以类型化内存视图作为成员的结构定义

    目前我正在尝试让一个具有类型化内存视图的结构能够工作 例如 ctypedef struct node unsigned int inds 如果 inds 不是内存视图 据我所知 它可以完美地工作 然而 通过内存视图并使用类似的东西 def
  • 为什么是 ”\?” C/C++ 中的转义序列?

    C C 中有四种特殊的非字母字符需要转义 单引号 双引号 反斜杠 和问号 显然是因为它们有特殊的含义 对于单身char 对于字符串文字 对于转义序列 但为什么是 其中之一 我今天读了教科书上的转义序列表 我意识到我已经never逃脱了 以前
  • 将按钮文本放在一行上

    我的按钮文本在 safari 中显示在一行上 即使在初次单击后 但是在 google chrome 上 当您第一次到达该按 钮时 我的按钮将显示在一行上 但是当您浏览更多帖子并再次遇到 加载更多 按钮时 文本搞砸了 这只发生在谷歌浏览器上
  • clang 实例化后静态成员初始化

    这样的代码可以用 GCC 编译 但 clang 3 5 失败 include

随机推荐

  • 常用的chrome配置参数

    让chromedriver不打开网页在后台进行 如果对chrome的启动参数感兴趣可以去看看脑补连接 from selenium import webdriver chrome options webdriver ChromeOptions
  • 解决pycharm安装python第三方库时遇到的问题——pycharm实体环境与虚拟环境

    目录 关于cmd打开cd操作的提示 1 pycharm虚拟环境和本地环境有啥区别 2 实体环境和虚拟环境怎么安装库 3 如何查询实体环境安装的库和虚拟环境安装的库 4 怎么切换本地环境或虚拟环境 5 总结使用pycharm时常见的3中环境
  • Jenkins插件开发之环境构建

    1 环境 1 1 jdk 1 1 1 下载 Java Platform Standard Edition 8 ReferenceImplementations 或其他途径下载 1 1 2 java环境配置 1 1 2 1 右键此电脑 属性
  • 【Python】实用小脚本

    本文整理了我在学习和工作中用到的实用python脚本 希望也能帮助到需要的小伙伴 文章目录 视频格式转换 pip快速下载命令 多进程处理百万图片数据集 视频格式转换 安装视频处理库moviepy pip install moviepy 安装
  • 【程序员面试金典】请设计一个算法,求出a和b点的最近公共祖先的编号。

    题目描述 有一棵无穷大的满二叉树 其结点按根结点一层一层地从左往右依次编号 根结点编号为1 现在有两个结点a b 请设计一个算法 求出a和b点的最近公共祖先的编号 给定两个int a b 为给定结点的编号 请返回a和b的最近公共祖先的编号
  • JavaWeb servlet的使用

    在jsp文件中没有java代码我们才算是学完啦 从EL表达式和JSTL标签 在减少在login jsp和index jsp中的java代码 而今天的学习是让在jsp中彻底没有java代码 原本写在doLogin jsp做登录判断的java代
  • 图像检索传统算法学习笔记

    图像检索领域传统算法学习笔记 与组内同学一起找到的一些图像检索传统算法 作一小结 以防忘记 性能统计 传统图像检索算法 CIFAR 10数据集mAP值 编码数不同 LSH局部敏感哈希 0 116 0 131 SH谱哈希 0 124 0 12
  • PhotoShop 之钢笔工具

    钢笔工具如下如 1 绘制直线 若按住Shift 键 单击鼠标左键可以绘制90度或者45度直线 按住Ctrl 并在空白处 单击鼠标左键 可退出绘制模式 2 绘制曲线 绘制第一个点单击 绘制第二个点的时候 按住鼠标左键并拖动即可绘制曲线 若想绘
  • 栈的讲解及实现(图解+代码/C语言)

    今天为大家分享的是栈的模拟实现 本文主要讲解如何以数组的形式模拟实现 同时给出链表模拟实现栈的代码 目录 图解栈的结构 数组模拟栈的分步实现 创建并初始化 入栈 检测栈是否为空 出栈 获取栈顶元素 获取栈内有效元素个数 销毁栈 链表模拟实现
  • 世界名着100部简介

    01 傲慢与偏见 02 孤星血泪 03 雾都孤儿 04 唐 吉诃德 05 安娜 卡列尼娜 06 飘 07 简 爱 08 悲惨世界 09 茶花女 10 基督山恩仇记 11 童 年 12 这里的黎明静悄悄 13 钢铁是怎样炼成的 14 战争与和
  • linux安装和卸载gcc g 4.8,CentOS 编译安装gcc 4.8 为了支持C++11新特性

    gcc属于gun软件 下载gun所有软件 1 编译gcc的时候 还是需要存在gcc g 2 等编译完成 卸载系统的gcc g 3 测试c 11 chunli CentOS sudoyuminstallgccgcc c chunli Cent
  • pathon爬虫,制作云图

    转载请标明出处 http blog csdn net forezp article details 70198541 本文出自方志朋的博客 今天一时兴起 想用Python爬爬自己的博客 通过数据聚合 制作高逼格的云图 对词汇出现频率视觉上的
  • 关于xilinx BRAM IP的延迟以及流程

    关于RAM IP的延迟 1 选择了output registers 可以在RAM输出端口添加register 也可以在core的输出添加 在primitives添加 降低clock to out到primitive的延迟 在core添加re
  • MySQL之数据库引擎详解(内附面试题:InnoDB和MyISAM的联系与区别)

    Welcome Huihui s Code World 接下来看看由辉辉所写的关于MySQL数据库引擎的相关操作吧 目录 Welcome Huihui s Code World 一 数据库引擎是什么 1 数据库引擎概念 2 最常见的引擎 I
  • Hive:Unable to open a test connection to the given database. JDBC url = jdbc:mysql://master12:3306

    hive启动不成功 一直报各种错 我是执行这条命令出的错 hive service metastore 这个问题困扰了我两三天一直没解决 网上找了各种方法基本都试过 还是不行 可能每个人的原因也不太一样吧 我说我的解决方法 可以试下 1 检
  • 曼哈顿算法公式_Manhattan Distance Calculation(曼哈顿距离算法)

    首先介绍一下曼哈顿 曼哈顿是一个极为繁华的街区 高楼林立 街道纵横 从A地点到达B地点没有直线路径 必须绕道 而且至少要经C地点 走AC和CB才能到达 由于街道很规则 ACB就像一个直角3角形 AB是斜边 AC和CB是直角边 根据毕达格拉斯
  • 【springboot+mybatisplus】分页查询-单表/联表

    参考链接 https www jianshu com p 0a21569f1e06 单表的分页查询用mybatisplus的selectPage就可以实现 联表的分页查询需要自己写sql语句 因为老哥不让写sql语句在DAO层 难看 所以写
  • 图书信息添加

    实现图书信息添加的添加功能并创建字符编码过滤器 避免中文乱码现象的产生 1 创建字符编码过滤器对象 创建字符编码过滤器对象 其名称为CharactorFilter类 该类实现了javax servlet Filter接口 并在doFilte
  • AD采样出来的数值与实际值之间的关系

    当刚接触AD采样时 一直对于AD采集出来的数值与实际的值之间的关系有些模糊 现在闲暇下来打算记录一下 这里以采集量为电压量来记录 当采集温度 电流等模拟量时 都是通过一个电路把模拟量转化为一个电压量输入进AD采样引脚 就不一一叙述 AD采样
  • 结构体对齐(内存对齐)

    本文转自 http www ksarea com articles 20071004 sizeof struct memory html 有的时候 在脑海中停顿了很久的 显而易见 的东西 其实根本上就是错误的 就拿下面的问题来看 struc