C语言再学习以内存对齐

昨天看Q3的代码,看到有个_INTSAIZEOF的宏,着实晕了一阵。一番google后,终于明白,这个宏的做用是求出变量占用内存空间的大小,先看看_INTSAIZEOF的定义吧:数据结构

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

(ANSI C标准下,_INTSAIZEOF宏定义在stdarg.h中,Q3中定义在bg_lib.h中;bg_lib.h -- standard C library replacement routines used by code)大数据

关于这个宏的内部原理,咱们后面再谈,仍是先理理“内存对齐”这词的意思吧,多年来一直模糊的存在于个人大脑中,究竟为何要内存对齐啊。google

内存对齐的根源spa

一、许多计算机系统对基本数据类型可容许地址做了必定的限制,要求某种类型对象的地址必须是某个值n(一般是二、四、8)的倍数,从而来简化处理器和存储器之间的接口的硬件设计。如Linux的对齐策略是2字节数据类型,例如short的地址必须是2的倍数。而较大的数据类型如:int、int*、float、double则必须是4的倍数。而Microsoft Windows的策略要求更为严格-----任何k字节对象的地址必须是k的倍数。好比要求一个double类型对象的地址必须是8的倍数(引自《深刻理解计算机系统》)。操作系统

二、数据结构(尤为是栈)应该尽量地在天然边界上对齐。缘由在于,为了访问未对齐的内存,处理器须要做两次内存访问;而对齐的内存访问仅须要一次访问。设计

 

内存对齐的规则code

上面所提到的天然边界是由什么决定的呢,我想应该是由硬件平台决定的,至于操做系统和编译器(默认对齐系数)则都是依赖于上一层的。固然,编译器的对齐系数能够经过预编译命令#pragma pack(n),n=1,2,4,8,16来改变;对象

举个例子:好比参数入栈,编译器并非一个紧挨着一个的压入栈的,而是根据对齐系数来压栈的,好比一个char类型的参数,虽然自己只占一个字节,可是编译器会自动补全后面3个字节,而后再压下一个参数。blog

 

(这里说点题外话:在写过delphi程序的人都知道,有个packed的保留字,做用就是压缩数据结构,不要按对齐存储,除非数据结构体积庞大,不然为何要用packed呢,用了不就影响内存读取的速度了吗?:))接口

 

一、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,之后每一个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。 
二、结构(或联合)的总体对齐规则:在数据成员完成各自对齐以后,结构(或联合)自己也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。 
三、结合一、2颗推断:当#pragma pack的n值等于或超过全部数据成员长度的时候,这个n值的大小将不产生任何效果。

(以上3点引自《也谈内存对齐》一文)

 

再看_INTSAIZEOF宏

这个宏的定义咋一看有点丈二和尚摸不着头脑,不过网上有对齐的解释,看后相信会豁然明朗了:

对于两个正整数 x, n 总存在整数 q, r 使得 x = nq + r, 其中  0<= r <n                  //最小非负剩余 q, r 是惟一肯定的。q = [x/n], r = x - n[x/n]. 这个是带余除法的一个简单形式。在 c 语言中, q, r 容易计算出来: q = x/n, r = x % n. 所谓把 x 按 n 对齐指的是:若 r=0, 取 qn, 若 r>0, 取 (q+1)n. 这也至关于把 x 表示为: x = nq + r', 其中 -n < r' <=0                //最大非正剩余 nq 是咱们所求。关键是如何用 c 语言计算它。因为咱们能处理标准的带余除法,因此能够把这个式子转换成一个标准的带余除法,而后加以处理: x+n = qn + (n+r'),其中 0<n+r'<=n            //最大正剩余 x+n-1 = qn + (n+r'-1), 其中 0<= n+r'-1 <n    //最小非负剩余 因此 qn = [(x+n-1)/n]n. 用 c 语言计算就是: ((x+n-1)/n)*n 若 n 是 2 的方幂, 好比 2^m,则除为右移 m 位,乘为左移 m 位。因此把 x+n-1 的最低 m 个二进制位清 0就能够了。获得: (x+n-1) & (~(n-1))

相关文章
相关标签/搜索