`
caoruntao
  • 浏览: 480858 次
  • 性别: 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求struct结构体内存大小的问题

      通过深入理解`sizeof`和`struct`结构体的内存对齐,开发者可以更好地控制内存使用,避免潜在的性能瓶颈,并优化代码的移植性。在实际编程中,尤其是涉及网络通信、数据序列化或硬件交互时,结构体的内存布局就显得尤...

      5分钟搞定内存字节对齐

      在C语言中,内存字节对齐是指编译器为了提高程序执行效率和可移植性,而对结构体成员在内存中的存储方式进行的调整。这个调整是基于体系结构的对齐规则,旨在提高程序的执行效率和可移植性。 在 C 语言中,sizeof ...

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

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

      C语言结构体的字节对齐及指定对齐方式.docx

      字节对齐是指在计算机内存中,结构体的成员变量按照一定的规则进行排列,以便提高存取效率和防止错误。不同的硬件平台对存储空间的处理方式不同,一些平台对某些特定类型的数据只能从某些特定地址开始存取。如果不...

      C语言每日一题:结构体的内存大小

      2. **结构体对齐**:整个结构体的大小也是其最大成员大小的倍数。这样可以避免在连续的数据块之间出现不必要的空白空间。 3. **联合体对齐**:联合体内的成员共享相同的内存空间,所以其大小取决于最大成员的大小,...

      结构体内存对其计算结构体大小

      在计算机科学中,内存对齐(Memory Alignment)是编程中一个重要的概念,特别是在处理结构体...在处理包含多种数据类型的结构体时,尤其需要关注内存对齐的影响,以避免不必要的内存浪费和潜在的运行时错误。

      C语言结构体的字节对齐原则[定义].pdf

      C语言结构体的字节对齐原则是指在计算机内存中,按照一定的规则将结构体成员变量排列的原则。这个原则的主要目的是为了提高程序的运行效率和避免硬件平台的限制。 在现代计算机中,内存空间都是按照 byte 划分的。...

      编译器数据对齐方式(结构体、类所占空间的计算)

      例如,`#pragma pack(1)`会改变默认的对齐参数为1,这会使得结构体成员之间的填充字节被去除,从而使sizeof(A)和sizeof(B)的值均为6。 此外,当一个结构体中没有任何成员,即为空结构体时,不同编译器可能会有不同...

      字节对齐与结构体大小

      在计算机编程中,字节对齐是一个非常重要的概念,特别是在涉及到结构体的内存布局时。结构体的`sizeof`操作符返回的是整个结构体在内存中占用的字节数,而这个值并不总是简单地等于所有成员大小的总和。这是因为...

      结构内存对齐StructMemory 配图示

      当结构体中的成员按对齐值对齐时,可以减少CPU访问内存的次数和时间,提高程序性能。 在C++中,结构体的内存布局受到编译器的影响,不同编译器可能会有不同的对齐策略。但一般来说,编译器会遵循以下原则: 1. ...

      C++内存对齐

      本文将深入探讨C++中的内存对齐机制,特别是结构体(`struct`)对齐方面,并提供具体的示例代码进行解释。 #### 二、内置类型的大小 内置类型的大小是指C++中基本数据类型在内存中占用的空间大小。这些类型包括但不...

      c语言中结构体等在计算机内存的对齐方式

      结构体在计算机内存中的对齐方式 在 C 语言中,结构体(struct)是一种自定义数据类型,用于组合多个变量以便更方便地组织和管理数据。但是,当我们使用 sizeof 运算符来获取结构体的大小时,经常会发现结果与预期...

      C语言内存对齐.pdf

      内存对齐是C语言中一个重要的概念,它与结构体(struct)和联合体(union)的内存布局密切相关。在计算机系统中,为了访问速度和硬件限制,数据往往需要按照一定的规则存储在内存中。如果数据的起始地址符合某个特定值...

      内存对齐详解

      - 结构体的对齐将根据`#pragma pack`指令指定的数值和结构体中最大数据成员的长度中的较小者来进行。 3. **特殊情况**: - 当`#pragma pack`指令指定的数值等于或超过所有数据成员的长度时,该数值的大小将不再...

      C中内存对齐

      在C语言中,内存对齐是一个重要的概念,它涉及到数据在计算机内存中的存储方式。内存对齐的主要目的是提高数据存取效率,减少CPU访问内存时的...了解内存对齐的原则和机制对于编写高效且跨平台的C语言代码至关重要。

      内存对齐(Memory alignment)

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

      C语言结构体的大小是如何计算的?(结构体对齐)

      ### C语言结构体的大小计算方法与结构体对齐 #### 一、使用`sizeof`计算结构体的大小 在C语言中,我们通常使用`sizeof`运算符来计算结构体的大小。例如,考虑以下结构体定义: ```c struct Student { int id; ...

      c++内存对齐,#pragma pack()对齐方式

      C++内存对齐是指在内存中存储变量或者结构体时,按照一定的规则排列数据,以提高存取效率和避免硬件平台相关的错误。本文详细介绍了C++内存对齐的原因和机制,并通过一个实例来演示VC对结构体的存储处理。 一、什么...

      定义嵌套结构体数组

      同时,我们需要注意结构体的内存对齐问题,因为编译器可能会在结构体成员之间插入额外的字节以保证数据访问的效率。这可能会影响到结构体的实际大小,可以通过 sizeof 操作符来获取结构体所占的字节数。 例如,我们...

    Global site tag (gtag.js) - Google Analytics