C语言ALIGN对齐数宏
C语言ALIGN对齐数宏
Linux内核中有许多 ALIGN 宏,如:
#define ALIGN(x, size) __ALIGN_MASK(x,(typeof(x))(size)-1) // typeof宏是gcc的扩展,用于求出x的类型
#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
int main() {
printf("原始值: %d\n", 3); // 3
printf("对齐后的值: %d\n", ALIGN(3, 4)); // 4
printf("原始值: %d\n", 4); // 4
printf("对齐后的值: %d\n", ALIGN(4, 4)); // 4
printf("原始值: %d\n", 7); // 7
printf("对齐后的值: %d\n", ALIGN(7, 4)); // 8
}
可见这个 ALIGN(x, size) 宏是完成将 x 按 size 的倍数对齐的:
- 如果
x是size的倍数,则已完成对齐 - 如果
x不是size的倍数,则向上对齐size的倍数
原理
计算字节对齐(比如计算视频画面的 stride),就是一个计算上界数的过程。先看例子:
-
计算a以size为倍数的下界数:
就让这个数(要计算的这个数)表示成二进制时,最后三位为0就可以达到这个目标。只要下面这个数与a进行"与运算"就可以了:
11111111 11111111 11111111 11111000 // 这个数实际下就是 ~(size - 1),可以将该数称为size的对齐掩码size_mask -
计算a以size为倍数的上下界数:
// 以下假设a和size数据类型相同 #define alignment_down(a, size) (a & (~(size-1)) ) #define alignment_up(a, size) ((a+size-1) & (~(size-1)))注: 上界数的计算方法,如果要求出比a大的是不是需要加上size就可以了?可是如果a本身就是size的倍数,这样加size不就错了吗,所以在a基础上加上(size - 1),然后与size的对齐掩码进行与运算
例如:
a=0, size=8, 则alignment_down(a,size)=0, alignment_up(a,size)=0.
a=6, size=8, 则alignment_down(a,size)=0, alignment_up(a,size)=8.
a=8, size=8, 则alignment_down(a,size)=8, alignment_up(a,size)=8.
a=14, size=8,则alignment_down(a,size)=8, alignment_up(a,size)=16.
注:size应当为2的n次方, 即2, 4, 8, 16, 32, 64, 128, 256, 1024, 2048, 4096 ...
再举个例子4k页面边界的例子,即a=4096: 如果x = 3888;那么以上界对齐,执行结果就是4096。 如果x = 4096;结果是4096. 如果x = 4222; 则结果为8192.
另外如果是以下界对齐,若x = 3888; 结果为0. 如果x = 4096;结果是4096. 如果x = 4222; 则结果为4096。
可以把这两种方式理解为“上进”和“丢弃”。
应用
在linux等代码中也常常可以看到,下面给出几个例子:
(1) 当分配地址addr时, 要将该地址以size为倍数对齐,而且要得到是比addr大的值(上界数),则使用 _ALIGN 宏
#define _ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1)))
(2) 页面对齐的 PAGE_ALIGN 宏(也是上界数)
#define PAGE_SIZE 4096
#define PAGE_MASK (~(PAGE_SIZE-1))
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1) & PAGE_MASK)

浙公网安备 33010602011771号