看http://blog.csdn.net/aihao1984/article/details/5953668这篇博文讲C的函数可变参数时,发现了以下用于做地址对齐的这段代码:
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统
这段代码做的事情就是,给定一个变量n,算出这个变量对齐到某个字长(整型的字节数)整数倍的字节数。这段代码有些难以理解。那么慢慢分析下吧。
假设有一个地址n,要把n按m对齐,无非就是找到大于等于n且整除m的最小的那个数。
我们定义一个宏函数F,它计算n按m对齐的结果,则按照上段代码的逻辑,F定义为:
#define F(n, m) (n+m-1)&~(m-1)
这段代码如果不用这种按位与来写,其实可以这么写:
#define F(n, m) (n+m-1)/m*m
以上的做法正确性可以分情况证明:
(为了说明问题方便,由于计算机里的除法和严格的数学意义上的除法是不一样的,我们这里以"/"表示计算机里的除法,"%"表示严格的数学除法,"mod"表示取模运算。之后也遵循这个惯例,只是代码还是遵循计算机语言本身的规范。)
1.如果n是能整除m,那么对齐结果应该就是n,能得出F(n, m)的结果正确;
2.如果n不能整除m,那么对齐结果应该是n - n mod m + m。由于n - n mod m + m能整除m,所以我们只需要证(n - n mod m + m)/m等于(n+m-1)/m。也即证
0<= (n+m-1) - (n - n mod m + m) < m
上面这个式子很显然。
当我们再审视计算机里的除法"/"时,发现,实际上,对于整数x与y,有:
x/y = (x - x mod y) % y
设a=n+m-1,有
(n+m-1)/m*m = a/m*m = (a - a mod m)%m*m = a - a mod m
实际上,只要证
a&~(m-1) = a - a mod m
当然,这里的前提是,m是2的幂次(因为m是整型的字节数,即字长)。
这里假设m=2^q。那么m的二进制表示一定是1后面跟q个0。m-1则为最后面q位为1,前面全为0,按位取反结果是后面q位为0,前面为1。
由于m是2的幂次,故a mod m结果就是a的二进制表示的最后q位结果。而a按位与一个前面全为1后q位为0的二进制数,则正好就是减去了后q位,等同于减去a对m的余数。
故得证。