`
caoruntao
  • 浏览: 467307 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

sizeof(结构体)和内存对齐

阅读更多

[转]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) 如果位域字段之间穿插着非位域字段,则不进行压缩;
备注:
结构体
typedef struct
{
char c:2;
double i;
int c2:4;
}N3;
在GCC下占据的空间为16字节,在VC下占据的空间应该是24个字节。
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

ps:

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

    相关推荐

      结构体大小-详解内存对齐问题

      该文档提供了详细解决结构体sizeof问题,从结构体内变量所占空间大小,默认内存对齐大小,强制内存对齐方法,变量在内存中布局的详细分析,语言言简意赅,绝无废话,为读者解决了大量寻找书籍的烦恼,读者可以花费几分钟的...

      5分钟搞定内存字节对齐

      写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?讲讲字节对齐吧.

      内存对齐(Memory alignment)

      内存对齐3.1 结构体成员默认内存对齐3.2 不同架构内存对齐方式3.3 小试牛刀3.3.1 前置填充3.3.2 中间填充3.3.3 尾随填充 1. 同个结构体占用内存可变化      在 C语言之结构体 章节里,对struct的功能和使用进行...

      字节对齐与结构体大小

      结构体的sizeof值,并不是简单的将其中各元素所占字节相加,而是要考虑到存储空间的字节对齐问题。这些问题在平时编程的时候也确实不怎么用到,但在一些笔试面试题目中出是常常出现,对sizeof我们将在另一篇文章中...

      C/C++结构体字节对齐详解

      结构体的sizeof  先看一个结构体:  struct S1  {  char c;  int i;  };  sizeof(s1)在VC6中按默认设置得到的结果为8。  我们先看看sizeof的定义——sizeof的结果等于对象或者类型所占的内存字节数...

      实例讲解C语言编程中的结构体对齐

      Q:关于结构体的对齐,到底遵循什么原则? A:首先先不讨论结构体按多少字节对齐,先看看只以1字节对齐的情况: #include #include #define PRINT_D(intValue) printf(#intValue is %d\n, (intValue)); #define ...

      深入理解c/c++ 内存对齐

      内存对齐一般讲就是cpu access memory的效率(提高运行速度)和准确性(在一些条件下,如果没有对齐会导致数据不同步现象).依赖cpu,平台和编译器的不同.一些cpu要求较高(这句话说的不准确,但是确实依赖cpu的不同),而有些...

      C语言结构体对齐.pdf

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

      详解C++ sizeof(下)

      sizeof作用于基本数据类型,在特定的平台和特定的编译器中,结果是确定的,如果使用sizeof计算构造类型:结构体、联合体和类的大小时,情况稍微复杂一些。 1.sizeof计算结构体 考察如下代码: struct S1 { char c; ...

      VC++中内存对齐实例教程

      为了优化CPU访问和优化内存,减少内存碎片,编译器对内存对齐制定了一些规则。但是,不同的编译器可能有不同的实现,本文只针对VC++编译器,这里使用的IDE是VS2012。 #pragma pack()是一个预处理,表示内存对齐。...

      C/C++语言中结构体的内存分配小例子

      当使用了 #pragma 指令指定编译器的对齐位数时,结构体按最长宽度的数据成员的宽度和 #pragma 指令指定的位数中的较小值对齐。 #pragma 指令格式如下所示:#pragma pack(4) // 或者 #pragma pack(push, 4) 举例如下...

      深入剖析C++中的struct结构体字节对齐

      但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因:各个硬件平台对存储空间的处理...

      ARM内存边界对齐以及sizeof问题

      默认情况下,在32位cpu里,gcc对于结构体的对齐方式是按照四个字节来对齐的。

      sizeof详细总结

      1、定义与语法 2、语法形式 3、基本数据类型的sizeof值 4、指针变量的sizeof值 5、数组的sizeof值 6、结构体的sizeof值(★★★★★) 7、含位域结构体的sizeof

      12.第十二章 自定义数据类型.txt

      结构体变量在内存空间中是连续存储的,结构体类型大小为sizeof(struct student),为各成员大小之和,各编译系统为内存管理方便可能分配大一些的内存空间来存储结构体,保证字节对齐。在Dev中上述结构体类型student...

      C/C++结构体对齐规则

       那么经过sizeof后得出结果是 12, 因为默认内存占用4byte, 变量a占用1byte,剩下3个,不足以存放变量b,故变量a也占用了4byte, 变量c由于后面没有了数据, 也会变为4byte,后sizeof值为12。 #include "stdafx....

      C++中类的内存空间大小(sizeof)分析

      在C语言中存在关于结构体的存储空间大小是比较深入的话题,其中涉及计算机的基本原理、操作系统等。我认为对齐是C语言中让很多初学者都拿不准摸不透的问题,特别是在跨平台的情况下,对齐这种问题更加的复杂多变,每...

      基于lex&yacc技术的C语言头文件的解析器(c head file parser)

      你的程序知道filehead_v1这个字符串,就能自动反推出结构体的内存,就可以自动在运行时,算出所需内存的大小,准备一块内存来装这个数据结构。 这里是我所实现的程序的一个最关键部件:C语言头文件解析器。 其中...

    Global site tag (gtag.js) - Google Analytics