C语言-结构体内存对齐

C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?

  开始学的时候,也被此类问题困扰很久。其实相关的文章很多,感觉说清楚的不多。结构体到底怎样对齐?

  有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):

  原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

  原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

  原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

  这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。

    例1:

struct {short a1;short a2;short a3;
} A;struct{long a1;short a2;
} B;

          sizeof(A) = 6; 这个很好理解,三个short都为2。

          sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。

    例2:

struct A{int a;char b;short c;
};struct B{char b;int a;short c;
};

       sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。

       sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。

   深究一下,为什么是这样,我们可以看看内存里的布局情况。

                 a         b         c
   A的内存布局:1111,     1*,       11

                 b          a        c
   B的内存布局:1***,     1111,   11**

   其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。

   B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

   再看一个结构中含有结构成员的例子:

       例3:

struct A{int a;double b;float c;
};struct B{char e[2];int f;double g;short h;struct A i;
};

       sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

       sizeof(B) = 48; 看看B的内存布局。

                                 e         f             g                h                                    i 
    B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *

    i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。

    以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;

    有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。

                                  a                b             c
       A的内存布局:1111,     11111111,   1111

                                 e        f             g          h                     i
       B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111

       那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。

       还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。

       使用位域的主要目的是压缩存储,其大致规则为:
       1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
       2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
       3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
       4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
       5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

        还是让我们来看看例子。

       例4:

struct A{char f1 : 3;char f2 : 4;char f3 : 5;
};

                     a        b             c
       A的内存布局:111,    1111 *,   11111 * * *

       位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。

       例5:

struct B{char f1 : 3;short f2 : 4;char f3 : 5;
};

       由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。

       例6:

struct C{char f1 : 3;char f2;char f3 : 5;
};

       非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。

       考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。

       最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。

转载于:https://www.cnblogs.com/JohnABC/p/4704789.html

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

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

相关文章

nginx 二进制包安装mysql_二进制安装mysql5.7

下载地址:https://downloads.mysql.com/archives/community/[rootlocalhost soft]# lsmysql-5.7.17-linux-glibc2.5-x86_64.tar.gz nginx-1.12.2 nginx-1.12.2.tar.gz[rootlocalhost soft]#1.详细描安装的过程1.1关闭防火墙systemctl stop firewalld.service #停止f…

.NET 类型(Types)的那些事

引言 您是.Net工程师?那 .NetFramework中的类型您知道有三大类吗?(除了引用类型和值类型,还有?) 引用类型一定在“堆”上,值类型一定在“栈”上? 那引用类型在内存中的布局细节您又知…

几种去除数组中重复元素的方法、数组去重

工作中遇到的一个问题&#xff0c;就是去除数组中重复的元素&#xff0c;记录一下几种有效的方法&#xff1a; 第一种思路&#xff1a;遍历要删除的数组arr, 把元素分别放入另一个数组tmp中&#xff0c;在判断该元素在arr中不存在才允许放入tmp中。 <!DOCTYPE html> <…

MongoDB学习使用

一、什么是MongoDB&#xff1f; MongoDB是一个高性能&#xff0c;开源&#xff0c;无模式的文档型数据库&#xff0c;是当前NoSql数据库中比较热门的一种。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式&#xff0c; NoSql&#xff0c;全称是 Not Only Sql,指的是…

域账号更改密码之后代理需要重新配置

在使用域账号的时候&#xff0c;如果需要配置账户和密码&#xff0c;那么最好记录下来&#xff0c;否则将来找不到就很尴尬了。 我遇到的问题是&#xff0c;因为在另外一台电脑配置了域账号&#xff0c;用来联网&#xff0c;提供网络给visual studio 1.Firefox 这个代理的账号…

wcf精通1-15

随笔- 197 文章- 0 评论- 3407 十五天精通WCF——第一天 三种Binding让你KO80%的业务 转眼wcf技术已经出现很多年了&#xff0c;也在.net界混的风生水起&#xff0c;同时.net也是一个高度封装的框架&#xff0c;作为在wcf食物链最顶端的我们所能做的任务已经简单的不能再简单…

python如何实现共享报表系统_使用python来实现报表自动化-阿里云开发者社区

xlwt 常用功能xlrd 常用功能xlutils 常用功能xlwt写Excel时公式的应用xlwt写入特定目录(路径设置)xlwt Python语言中&#xff0c;写入Excel文件的扩展工具。可以实现指定表单、指定单元格的写入。支持excel03版到excel2013版。使用时请确保已经安装python环境。百度百科xlrd Py…

去除inline-block元素间间距的N种方法

这篇文章发布于 2012年04月24日&#xff0c;星期二&#xff0c;22:38&#xff0c;归类于 css相关。 阅读 147771 次, 今日 52 次 by zhangxinxu from http://www.zhangxinxu.com 本文地址&#xff1a;http://www.zhangxinxu.com/wordpress/?p2357 一、现象描述 真正意义上的in…

Docker深入浅出2

Docker系统架构 Docker使用客户端-服务端&#xff08;c/s&#xff09;架构模式&#xff0c;使用远程api来管理和创建Docker容器。 docker容器通过Docker镜像来创建。 容器与镜像的关系类似于面向对象编程中的对象与类的关系 Docker面向对象容器对象镜像类加速器配置&#xff1a…

mysql安装包下载密码_MySQL解压包的安装与下载的图文教程

这篇文章主要为大家详细介绍了mysql解压包的安装基础教程&#xff0c;具有一定的参考价值&#xff0c;感兴趣的小伙伴们可以参考一下由于换了新电脑&#xff0c;所以的环境都要到新电脑去配置。突然发现mysql的配置忘了&#xff0c;然后百度又重新来一遍。特地写一篇文章记录一…

php 扒取网页数据

扒取方法 public function index(){$url http://www.dytt8.net/;// $url Public/txt/movies.txt;$content file_get_contents($url);$content iconv("gb2312", "utf-8//IGNORE",$content);$reg "|<div class\"co_content2\">(.*…

多维DP UVA 11552 Fewest Flop

题目传送门 1 /*2 题意&#xff1a;将子符串分成k组&#xff0c;每组的字符顺序任意&#xff0c;问改变后的字符串最少有多少块3 三维DP&#xff1a;可以知道&#xff0c;每一组的最少块是确定的&#xff0c;问题就在于组与组之间可能会合并块&#xff0c;总块数会-1。…

多表联合查询

关联数据库字典表的多表联合查询 inner join…on 自动连接 需要用到表的所有信息时&#xff0c;可以用以下两种方法 1) left join…on… 左连接 &#xff08;以左为准&#xff0c;右边没有NULL代替&#xff09; 2) right join…on… 右连接&#xff08;以右为准&#xff…

python elasticsearch update_使用python的elasticsearch部分更新

我有以下格式的elasticsearch文档。我需要部分更新“x”字段并在其中添加python dict。{"_index": "gdata34","_type": "gdat","_id": "328091-72341-118","_version": 1,"_score": 1,"…

32位与64位注册表

如果32位系统OFP的注册表路径是 HKEY_LOCAL_MACHINE\SOFTWARE\Bohemia Interactive\ 那么在64系统里就应该是 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Bohemia Interactive\ 多了一级Wow6432Node转载于:https://www.cnblogs.com/zhang-pengcheng/p/4712135.html

http 请求头和响应头

客户端发送请求过程带着的数据&#xff1a; 1.请求地址 2.请求方式 3.请求头 request headers 4.请求参数 https://www.juhe.cn/ 130.... 1a2b....pei 服务端响应给客户端的信息&#xff1a; 1.响应内容 2.响应报文/响应头部 response headers a 响应头 b 响应体 3.http状…

[算法]-排序算法之希尔排序

希尔排序算法思想 希尔排序的实质就是分组插入排序&#xff0c;该方法又称缩小增量排序.基本思想是&#xff1a;先将整个待排元素序列分割成若干个子序列&#xff08;由相隔某个“增量”的元素组成的&#xff09;分别进行直接插入排序&#xff0c;然后依次缩减增量再进行排序&a…

python tkinter button颜色变不了_tkinter多按钮颜色变化

我使用tkinter创建一个8x8按钮矩阵&#xff0c;当按下单个按钮时&#xff0c;它会添加到最终列表中(例如finalList((0,0)&#xff0c;(5,7)&#xff0c;(6,6)&#xff0c;…)&#xff0c;允许我快速创建8x8(x&#xff0c;y)坐标图像。我已经创建了一个带有按钮的窗口&#xff0…

应用spss可靠性分析软件

问卷调查的可靠性分析 一、概念&#xff1a; 信度是指依据測验工具所得到的结果的一致性或稳定性&#xff0c;反映被測特征真实程度的指标。一般而言&#xff0c;两次或两个測验的结果愈是一致。则误差愈小&#xff0c;所得的信度愈高&#xff0c;它具有下面特性&#xff1a;1、…

springmvc中的单例问题

1&#xff0c;springmvc实际上是基于一个叫做DispatcherServlet的servlet的。servlet按照以往的学习经验&#xff0c;他是单事例多线程的。 Servlet生命周期 1.装载Servlet。这项操作一般是动态执行的。然而&#xff0c;Server通常会提供一个管理的选项&#xff0c;用于在Serve…