1. 重要概念

内存对齐:系统对基本类型数据在内存中存放的位置都有限制,它们会要求这些数据的首地址的值是某个数K的倍数。

对齐模数:结合内存对齐的概念,这个k值就是对齐模数。

注意:对齐模数的具体取值与编译器有关。

对其要求严格强度:当一种类型的对齐模数S与另一种类型的对齐模数T的比值大于1时,我们就称类型S要求比T严格(强),称T比S宽松(弱)。

内存对齐优点:简化处理器与内存之间传输系统的设计,提升读取数据的速度。所以,如果想要提升性能,那么所有的程序数据都应该尽可能地对其。

填充区:是结构体字段满足内存对齐要求而额外分配给结构体的空间。

ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部填充区大小之和。

ANSI C规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,但可以更严格。

结构体和共用体的对齐模数就是该结构体中最严格类型的对齐模数。

2. 不同编译器的内存对齐规则

测试环境:

Windows7        Visual Studio2010  10.00

Ubuntu10.04    GCC gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3

2.1 VC/VS下的结构体

默认对齐规则:任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。

在VC/VS中,变量对齐规则如下:

char(8-bit)                    在字节边界上对齐

short(16-bit)                在双字节边界上对齐

int and long(32-bit)     在4字节边界上对齐

float(32-bit)                 在4字节边界上对齐

double(32-bit)             在8字节边界上对齐

下面是VC/VS与GCC共同默认的情况:

struct x                        与该结构体中最大对齐模数的类型相同

union x                        与该共用体中最大对齐模数的类型相同

1) 按照上面的规则,我们先来分析一下下面的一个结构体:

struct st1 {

char    a;

short    b;

int        c;

};

它在内存中的大致布局如下:

由于a是单字节对齐,所以可以防止在任何位置。而b是双字节对齐,不能放在奇数地址上,所以不能紧接着放在a的后面,而是先要在a后面填充一个字节空间,然后才是b的空间。这样a和b共同占用了4个字节,最后c占用了4个字节的空间,由于c的对齐模数是该结构体中最大的对齐模数,a、填充区域再加b,刚好满4个字节,此时不再需要新的填充区域,因此整个结构体的大小就是1+1+2+4=8。

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:   0046FEF4

short b: 0046FEF6

int c:      0046FEF8

sizeof(struct st1) = 8

2) 交换一下顺序,结果就会不同:

struct st2 {

char    a;

int        b;

short    c;

};

其在内存中的布局如下:

c需要放在4字节边界上,也就是相对于起始地址偏移量为4或者4的倍数的位置上放置,现在a在起始位置,a后面需要填充3个区域才能开始放c。当c放好后,需要放b,b占用两个字节,那此时是否需要在b后面加上填充区域呢?

C标准规定,任何类型(包括自定义结构体类型)的数据所占用空间的大小一定等于一个单独的该类型数据的大小乘以数据元素的个数。换句话说,就是各个元素之间不会有空隙的。如果b之后不填充那两个字节,那么假如有一个struct st2类型的数组,它在内存中的布局将不满足上述要求,因为第二个元素的字段a是紧接着b开始的,这样最终元素的实际空间将小于按照规定得出的空间大小。

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:   0048FE4C

short b: 0048FE50

int c:      0048FE54

sizeof(struct st2) = 12

3) 如何就能断定,在VC/VS下默认的对齐模数就是最大的字段类型的对齐模数呢?经过上面的两个例子,下面的结构体应该很容易得到结果了

struct st3 {

char       a;

double   b ;

};

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:     0051FDD4

double b: 0051FDDC

sizeof(struct st3) = 16

可以清楚的发现,a后面填充了7个字节的空间,这是由于该结构体将8作为其对齐模数了。后面我会将GCC对齐规则展示。

4)如果结构体中有数组,则其对齐模数仍然按照单个元素的对齐模数来算:

struct st3 {

char    a;

char    b[8];

double    c;

};

b数组类型是char,所以其对齐模数是1,可以作为a的填充,将前7个元素填充到a之后,最后一个元素放到下一个8字节区间,需要填充7位,接着放c。由此,该结构体最后的大小为24。

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:      0022F78C

char b[7]: 0022F78D

double c:  0022F79C

sizeof(struct st3) = 24

换成int类型数组原理一样:

struct st3 {

char        a;

int           b[2];

double    c;

};

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:    0053F82C

int b[2]:  0053F830

double c: 0053F83C

sizeof(struct st3) = 24

5) 对于包含有其他结构体或者共用体的复杂结构体,且看下面的例子:

struct st4 {

char            a;

int               b;

struct st3    c;

};

先分析一下,这个结构体应该占多少字节呢?显然,对齐模数要求最严格的是c,那么c该怎么算呢?是16吗?如果是16,那么结果是否应该是32?非也。

其实c的大小的确是16,但是c的对齐模数是8,按照规定,st4种的对齐模数便是8。因此a与b凑齐8个字节即可。因此最终st4所占用字节数为24。

其在内存中的布局如下:

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:         0016FD34

int b:            0016FD38

struct st3 c: 0016FD3C

sizeof(struct st4) = 24

若该结构体包含有共用体,计算方法与包含结构体成员情况类似,不同之处仅仅在于共用体成员的大小计算方法不同。共用体大小的计算方法看2.2部分。

2.2 VC/VS下的共用体

共用体的结构与结构体非常类似,不同点在于前者是所有成员共享同一段内存空间,而后者则单独分配内存空间。二者的对齐规则是相同的, 只不过在选取存储区域的时候按照最大对齐模数来寻找最大所需存储区。

1)先来看一个简单的例子

union un0 {

char        a;

double    b;

};

在这个共用体中,最大的对齐模数是8,而b刚好占用8个字节,所以该共用体最后的大小就是8。

char a:    002EFA5C

double b: 002EFA5C

sizeof(union un0): 8

可以看到共用体所有成员均是从同一地址开始的——共享同一存储区。

2)对于数组情况,则不能像结构体中那样看单元所占用空间大小了,而是要看数组总共占用的内存大小,因为共用体最终是要寻找一块可以容纳其最大的成员的内存空间。

union st2 {

char    a;

int      b[2];

};

对齐模数是4,b占用8个字节,因此该共用体应该为8。由于各成员的起始地址均相同,所以以后的例子不再列举个成员地址。

3)包含有其他共用体或者结构体的情况

不论包含的是结构体还是共用体,都需要计算该成员所需内存空间,结构体需要的内存计算方法在前面已经提到过,共用体的大小与其最大成员占用空间大小一致。结构体需要计算所有成员占用内存空间,而共用体只需要计算最大成员的内存空间即可。

union un2 {

char    a;

int      b[2];

};

union un4 {

short          a;

union un2   b;

};

un4的最大对齐模数与union un2相同,为4,而b的大小为8,因此这个共用体最后大小也为8。

struct st2 {

char      a;

double  b;

int         c;

};

union un2 {

char           a;

struct st2   b;

int              c;

};

sizeof(struct st2)=24; sizeof(union un2)=24

2.3 GCC下的结构体

对齐规则:(K代表对齐模数,T代表基本数据类型)

<=2:K=sizeof(T);

>2:K=4。

即小于等于2的时候,按照类型本身大小来算,而大于2的类型一律将4作为对齐模数。

1)为了验证该规则,并与VC/VS做一个对比,将VC/VS下测试的例子在GCC下测试:

struct ms1 {

char        a;

double    c;

};

如果按照8字节对齐,那么a之后需要填充7个字节,也就是c的起始地址应该是从下一个8字节区间开始,那最终结果应该是16,我们来看看测试结果:

char a:     0xbff49654

double c: 0xbff49658

sizeof(struct ms1): 12

发现c是从下一个4字节区间开始的,上述对齐规则得证。

当sizeof(T)<2时:

struct ms3 {

char    a;

char    b[2];

};

有:

char a:    0xbfff7c11

short b:   0xbfff7c12

sizeof(struct ms3): 3

当sizeof(T)=2时:

struct ms4 {

char    a;

short    b;

};

有:

char a: 0xbff71f1c

short b: 0xbff71f1e

sizeof(struct ms4): 4

2)根据上面的规则,下面的结构体应该很容易判断

struct ms1 {

char        a;

short       b;

double    c;

};

因为a小于2字节,因此需要填充1个字节,而b刚好2个字节,c是8个字节,按规定其对齐模数为4,因此“拆”成2半,这样的话,a与b共同凑成4个字节,此结构体最终的小就是12。

内存布局如下:

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

char a:     0xbfb6d984

short b:   0xbfb6d986

double c: 0xbfb6d988

sizeof(struct ms1): 12

3)带有字符数组的结构体

struct ms1 {

double   a;

int          b;

char       c;

char       d[9];

};

下面是各字段在内存中的地址以及最终该结构体所占用的空间:

double a:  0xbfd7c2f0

int b:         0xbfd7c2f8

char c:       0xbfd7c2fc

char d[9]:  0xbfd7c2fd

sizeof(struct ms1): 24

4)包含其他结构体或者共用体的情况下,依然按照最初设定的规则,超过4字节,一律按照4字节处理,因此最终复合结构体的大小也很容易算出,这里就不再举例了。

GCC下的共用体

在GCC下,共用体依然遵守同结构体相同的对齐规则。所有数据类型依然以2字节为界限。

经过上面的讨论,GCC下的共用体应该不用多说了。在此我只总结性的讲一下如何计算,例子就不举了。

首先要计算看哪个成员的对齐模数最大,选取最大的对齐模数,然后看占用最大空间的成员的内存空间是否为该对齐模数的整数倍,若不是,填充若干字节直到达到该对齐模数的最小公倍数。

如果该共用体中含有结构体或者共用体成员,需要求得该成员的对齐模数。结构体和共用体的对齐模数与其自身成员的最大对齐模数相同。需要注意的是共用体所占用的内存为与其占用内存最大的成员的内存空间相同。

linux 内存对齐,Windows和Linux下的字节对齐(转载)相关推荐

  1. 三步解决C语言中struct字节对齐问题,Python进阶篇-struct字节对齐问题

    Python进阶篇-struct字节对齐问题 Python进阶篇-struct字节对齐问题 Python调用C的时候,会传递一些复杂的数据结构,例如结构体,这时候就会遇到各种各样字节对齐的问题.下边所 ...

  2. java socket windows linux,socket在windows下和linux下的区别

    windows到Linux代码移植遇到的问题 1.一些常用函数的移植http://www.vckbase.com/document/viewdoc/?id=1586 2.网络------转载& ...

  3. windows下的库文件在linux的使用,Windows、Linux之间传输文件的几种方式

    常见的文件传输协议有ftp.sftp,sftp就是在ftp的基础上对传输的数据进行了加密. ftp速度快,sftp速度略慢但安全性高. ftp默认使用21端口,sftp默认使用22端口. 我使用的是C ...

  4. linux内存换算成b显示,Linux内存管理(下)

    Linux内存管理(下)物理内存管理(页管理) Linux内核管理物理内存是通过分页机制实现的,它将整个内存划分成无数4k(在i386体系结构中)大小页,从而分配和回收内存的基本单位便是内存页了.利用 ...

  5. windows和linux主机名,windows和linux下如何远程获取操作系统版本和主机名

    远程获取windows和linux操作系统版本和主机名需要具备以下条件: 假设 主机A(windows 7),ip:192.168.12.2 主机B(centos 6.3),ip:192.168.12 ...

  6. linux里运行windows,在Linux上运行Windows应用程序

    当前位置:我的异常网» Linux/Unix » 在Linux上运行Windows应用程序 在Linux上运行Windows应用程序 www.myexceptions.net  网友分享于:2015- ...

  7. windows兼容Linux php,支持windows与linux的php计划任务的实现方法

    本文实例讲述了支持windows与linux的php计划任务的实现方法.包括了在winows下利用winodows计划任务来操作,还有在linux中利用linux的方法来实现.分享给大家供大家参考.具 ...

  8. linux 内存占用_分享Linux内存占用几个案例

    案例一 问题 最近一台 CentOS 服务器,发现内存无端损失了许多,free 和 ps 统计的结果相差十几个G,非常奇怪,后来Google了许久才搞明白. 分析 1.linux系统内存消耗主要有三个 ...

  9. Linux内存描述之概述--Linux内存管理(一)

    1 前景回顾 1.1 UMA和NUMA两种模型 共享存储型多处理机有两种模型 均匀存储器存取(Uniform-Memory-Access,简称UMA)模型 将可用内存以连续方式组织起来, 非均匀存储器 ...

  10. windows文件 linux cr,DOS/Windows和Linux/Unix间的文件格式转换

    做无线传感器网络自己生产拓扑的时候一直碰到看起来相同的文件被解析成拓扑的时候却不同,一直摸不清原因,居然是这个问题,还好看见了这篇文章. DOS/Windows和Linux/Unix的文件换行回车格式 ...

最新文章

  1. 【ACM】杭电OJ 1284(待更)
  2. 现金奖励+实习offer!数据库大赛来了
  3. Pokémon AI,使用DALL-E生成神奇宝贝图鉴
  4. 获取事件相对于文档的位置
  5. Bat 循環執行範例
  6. 手机浏览器页面知识收集
  7. mongodb创建用户名和密码_Python中使用MongoDB详解
  8. garch预测 python_安利几个非常实用的 Python 库
  9. node --- 模块加载机制
  10. zookeeper的单实例和伪集群部署
  11. 走近华为“天才少年”钟钊:入职两年两度突破业界学界极限
  12. ultraedit教程java_Java开发工具配置UltraEdit基础教程
  13. 【数据结构与算法】算法的空间复杂度
  14. Raid及mdadm命令
  15. eviews 9.5新版本——平均预测、面板效应检验
  16. linux整盘远程备份,linux实现自动远程备份(scp+ssh)
  17. python websockets(wss)
  18. oracle取较小数,oracle 取小数位数
  19. IT外包服务内容介绍
  20. Mixpanel接入

热门文章

  1. 为你的.NET/Mono应用程序加入更新支持NetSparkle
  2. NSIS nsDialogs 插件
  3. 「Leetcode」206.反转链表:听说过两天反转链表又写不出来了?
  4. 彻底理解程序为什么会超时
  5. vim 命令整理(自己常用)
  6. 在苹果Mac中如何不用鼠标刪除文件?
  7. 如何在苹果Mac上的多个空间中工作?
  8. 如何解决Mac上安装Axure首次打开报错的问题?
  9. 如何恢复出厂设置并还原Apple Silicon M1 Mac?
  10. 我的gpu_tensorflow和cuda配置过程