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) 宏是完成将 xsize 的倍数对齐的:

  • 如果 xsize 的倍数,则已完成对齐
  • 如果 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)
posted @ 2025-05-07 10:32  3的4次方  阅读(70)  评论(0)    收藏  举报