本帖最后由 laocuo1142 于 2024-12-4 10:39 编辑
5.2 对齐时的填充字节代码如下: struct A
{
char c;
int i;
short s;
};
int main(void)
{
struct A a;
a.c = 1; a.i = 2; a.s = 3;
printf("sizeof(A)=%d\n", sizeof(struct A));
return 0;
}
执行后输出为sizeof(A)=12。 VC6.0环境中,在main函数打印语句前设置断点,执行到断点处时根据结构体a的地址查看变量存储如下:
可见填充字节为0xCC,即int3中断。
5.3 pragma pack语法说明#pragma pack(n)
#pragma pack(push, 1)
#pragma pack(pop)
该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由/ Z p选项设置。紧凑对齐用pack编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或者联合说明处生效。该编译指示对定义无效。 当使用#pragma pack (n) 时,n 为1、2、4、8 或1 6 。第一个结构成员后的每个结构成员都被存储在更小的成员类型或n字节界限内。如果使用无参量的#pragma pack,结构成员被紧凑为以/ Z p指定的值。该缺省/ Z p紧凑值为/ Z p 8。 2)编译器也支持以下增强型语法: #pragma pack( [ [ { push | pop } , ] [identifier, ] ] [ n] )
若不同的组件使用pack编译指示指定不同的紧凑对齐, 这个语法允许你把程序组件组合为一个单独的转换单元。 带push参量的pack编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。编译指示的参量表从左到右读取。如果使用push,则当前紧凑值被存储起来;如果给出一个n值,该值将成为新的紧凑值。若指定一个标识符,即选定一个名称,则该标识符将和这个新的的紧凑值联系起来。 带一个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并使该值为新的紧凑对齐值。如果使用pop参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值,并将产生一个警告信息。若使用pop且指定一个n值,该值将成为新的紧凑值。 若使用pop且指定一个标识符,所有存储在堆栈中的值将从栈中删除,直到找到一个匹配的标识符。这个与标识符相关的紧凑值也从栈中移出,并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符, 将使用命令行设置的紧凑值,并且将产生一个一级警告。缺省紧凑对齐为8。 pack编译指示的新的增强功能让你在编写头文件时,确保在遇到该头文件的前后的紧凑值是一样的。 5.4 Intel关于内存对齐的说明以下内容节选自《Intel Architecture 32 Manual》。 字、双字和四字在自然边界上不需要在内存中对齐。(对于字、双字和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。) 无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。 一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。 某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常(#GP)。双四字的自然边界是能够被16 整除的地址。其他操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。 5.5 不同架构处理器的对齐要求RISC指令集处理器(MIPS/ARM):这种处理器的设计以效率为先,要求所访问的多字节数据(short/int/ long)的地址必须是为此数据大小的倍数,如short数据地址应为2的倍数,long数据地址应为4的倍数,也就是说是对齐的。 CISC指令集处理器(X86):没有上述限制。 对齐处理策略 访问非对齐多字节数据时(pack数据),编译器会将指令拆成多条(因为非对齐多字节数据可能跨越地址对齐边界),保证每条指令都从正确的起始地址上获取数据,但也因此效率比较低。 访问对齐数据时则只用一条指令获取数据,因此对齐数据必须确保其起始地址是在对齐边界上。如果不是在对齐的边界,对X86 CPU是安全的,但对MIPS/ARM这种RISC CPU会出现“总线访问异常”。 为什么X86是安全的呢? X86 CPU是如何进行数据对齐的。X86 CPU的EFLAGS寄存器中包含一个特殊的位标志,称为AC(对齐检查的英文缩写)标志。按照默认设置,当CPU首次加电时,该标志被设置为0。当该标志是0时,CPU能够自动执行它应该执行的操作,以便成功地访问未对齐的数据值。 然而,如果该标志被设置为1,每当系统试图访问未对齐的数据时,CPU就会发出一个INT 17H中断。X86的Windows 2000和Windows 98版本从来不改变这个CPU标志位。因此,当应用程序在X86处理器上运行时,你根本看不到应用程序中出现数据未对齐的异常条件。 为什么MIPS/ARM不安全呢? 因为MIPS/ARM CPU不能自动处理对未对齐数据的访问。当未对齐的数据访问发生时,CPU就会将这一情况通知操作系统。这时,操作系统将会确定它是否应该引发一个数据未对齐异常条件,对vxworks是会触发这个异常的。
|