牛腱子可以炖汤吗:内存对齐与ANSI C中struct内存布局
来源:百度文库 编辑:偶看新闻 时间:2024/05/18 14:20:49
内存对齐与ANSI C中struct内存布局
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k(通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。
当一种类型S的对齐模数与另一种类型T的对齐模数的比值是大于1的整数,我们就称类型S的对齐要求比T强(严格),而称T比S弱(宽松)。这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节内存块上。某些处理器在数据不满足对齐要求的情况下可能会出错,但是Intel的IA32架构的处理器则不管数据是否对齐都能正确工作。不过Intel奉劝大家,如果想提升性能,那么所有的程序数据都应该尽可能地对齐。 Win32平台下的微软C编译器(cl.exe for 80x86)在默认情况下采用如下的对齐规则: 任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型(比如Long和double)都以4为对齐模数。 现在回到我们关心的struct上来。ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。那么结构体本身也有对齐要求,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格(但此非强制要求,VC7.1就仅仅是让它们一样严格)。我们来看一个例子(以下所有试验的环境是Intel Celeron 2.4G + WIN2000 PRO + vc7.1,内存对齐编译选项是"默认",即不指定/Zp与/pack选项): typedef struct ms1 { char a; int b; } MS1;MS1中有最强对齐要求的是b字段(int),所以根据编译器的对齐规则以及ANSIC标准,该结构体的内存布局图如下:一组可能的对齐模数数据如下: 数据类型 模数
------------------
char 1
shor 2
int 4
double 8 ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。
注:填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。 产生填充区的条件:
当结构体中的成员一种类型S的对齐模数与另一种类型T的对齐模数不一致的时候,才可能产生填充区。我们通过编译选项设置/zpn 或#pragma pack(push, n) 来设置内存对齐模数时,当结构体中的某中基本数据类型的对齐模数大于n时才会影响填充区的大小,否则将会按照基本数据类型的对齐模数进行对齐。
例子: 当n = 1时:
#pragma pack(push,1)
typedef struct ms3{char a; short b; double c; } MS3;
#pragma pack(pop) 这时n=1,此结构中基本数据类型short的对齐模数为2,double为8,大于n 所以将会影响这两个变量存储时地址的偏移量,必须是n的整数倍,而char的对齐模数是1,小于等于n,将会按照其自身的对齐模数1进行对齐.
因为n=1,所以这三个变量在内存中是连续的而不存在填充区.内存布局如下:
___________________________
| a | b | c |
+-------------------------+
Bytes: 1 2 8 sizeof(MS3) = 11 当n = 2时:
#pragma pack(push,2)
typedef struct ms3{char a; short b; double c; } MS3;
#pragma pack(pop) 这时n=2,此结构中基本数据类型double的对齐模数为8,大于n, 所以将会影响这个变量存储时地址的偏移量,必须是n的整数倍,而char 和 short 的对齐模数小于等于n, 将会按照其自身的对齐模数分别是1,2进行对齐.内存布局如下:
____________________________
| a |\| b | c |
+---------------------------+
Bytes: 1 1 2 8 此时变量c的存储地址偏移是4,是n=2的整数倍,当然偏移为6,8等等时也满足这个条件,但编译器不至于愚蠢到这种地步白白浪费空间,呵。 sizeof(MS3) = 12 当n = 4时:与n=2时结果是一样的. 当n = 8时:
#pragma pack(push,8)
typedef struct ms3{char a; short b; double c; } MS3;
#pragma pack(pop) 这时n=8,此结构中char ,short ,double的对齐模数为都,小于等于n,将会按照其自身的对齐模数分别是1,2,8进行对齐.即:short变量存储时地址的偏移量是2的倍数;double变量存储时地址的偏移量是8的倍数.
内存布局如下:
_______________________________________
| a |\| b |\padding\| c |
+-------------------------------------+
Bytes: 1 1 2 4 8 此时变量a的存储地址偏移是0,当然也是char型对齐模数1的整数倍了 变量b的存储地址偏移要想是short型对齐模数2的整数倍,因为前面a占了1 个byte ,所以至少在a 与b之间再加上1 个byte的padding.才能满足条件。 变量c的存储地址骗移要想是double型对齐模数8的整数倍,因为前面a 和b 加 1个byte 的padding,共4 bytes所以最少还需要4 bytes的padding才能满足条件。 sizeof(MS3) = 16 当n = 16时:与n=8时结果是一样的. ====================================================
根据上面的分析,如下定义的结构
#pragma pack(push, 2)
struct s
{
char a;
};
#pragma pack (pop) 因为char 的对齐模数是1,小于n=2,所以将按照自身的对齐模数对齐。根本就不会存在填充区,所以sizeof(s) = 1.对于s c[2]; sizeof(c)==2 也是必然的。
再看下面的结构:
#pragma pack(push, n)//n=(1,2,4,8,16)
struct s
{
double a;
double b;
double c;
};
#pragma pack (pop) 对于这样的结构无论pack设置的对齐模数为几都不会影响其大小,即无padding. double 类型的对齐模数为8
当n<8时,虽然满足前面讲的规则:当结构体中的某中基本数据类型的对齐模数大于n时才会影响填充区的大小。但这个时候无论n等于几(1,2,4),double 变量存储时地址的偏移量都是n的整数倍,所以根本不需要填充区。当n>=8时,自然就按照double 的对齐模数进行对齐了.因为类型都一样所以变量之间在内存中不会存在填充区.---------------------------------------------------------------------------------------------------------------------补充一点:如果在定义结构的时候,仔细调整顺序,适当明确填充方式,则最终内存结果可以与编译选项/Zpn 或 pack无关。举个例子:typedef struct ms1{ char a; char b; int c; short d; } MS1;在不同的 /Zpn下,sizeof(MS1)的长度可能不同,也就是内存布局不同。如果改成typedef struct ms2{ char a; char b; short d; int c; } MS2;即便在不同的/Zpn或pack方式下,编译生成的内存布局总是相同的; 再比如:typedef struct ms3{ char a; char b; int c; } MS3;可以改写成:typedef struct ms4{ char a; char b; short padding; int c; } MS4; 显式地写上 padding (通过源代码本身来消除隐患,要比依赖编译选项更加可靠,并易于移植,优质的代码应该做到这一点)(减少隐含padding的另外一个好处是少占内存,当结构的实例数量很大时,内存的节省量是非常可观的)(以上的变量/结构命名没有遵循命名规范,只为说明用,不可模仿)
ANSI C (99标准)与(93标准)有何不同
c语言struct HAR是什么意思?
核心内存与物理内存
电脑运行快慢或内存与C盘有关吗?
哪位大虾给我一些c语言中有关struct的知识?
c.堆内存(Heap memory)与栈内存(Stack memory)有什么区别?
C盘内存少
C盘内存问题
旭日125C内存
关于C盘内存
C语言中怎样访问和修改物理内存
什么是C语言中内存溢出?关于malloc函数的~
在EXCEL中怎么对齐图中的数据?图中的点与坐标要对齐,
内存中 现代的 pc2700与pc3200可否换用?
比特精灵中磁盘与内存选项怎样设置较好?
内存与硬盘
处理器与内存问题。
内存与显存
主板与内存
CPU与内存
CPU与内存问题
物理内存与虚拟内存!!
分辨率与内存关系
虚拟内存与内存