计算向向上取整的

由于两数相除,默认是向下取整,而这里是向上取整数

#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

参考​

计算向下取整的整数倍数

如:m = 5  n = 3,3的整数倍有3,6,9,12,但是对于5向下取整为3

解决:先用m/n得到商,然后再剩以n

#define DOWN_INT(M, N) (((M) / (N)) * (N))

计算向上取整的整数倍数

如:m = 5  n = 3,3的整数倍有3,6,9,12,但是对于5向上取整为6

解决:先用m/n得到商,然后(商+1)再剩以n

#define UP_INT(M, N) ((((M) + ((N) - 1)) / (N)) * (N))

当n为2的n次方时,改进上面的算法

其实这是Linux用来对指针做字节对齐的宏

#define ALIGN(x, a)           __ALIGN_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_MASK(x, mask) ( ((x) + (mask)) & ~(mask) )
#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a)))

核心点在于a为  Linux常用内核宏_宏定义,所有  a - 1 再取反 后低位全为  0  高位全为1

举例:x = 5  a = 4(4   -->>    1111 1100)

由于高位不管是取0还是取1都是都是4的整数倍,如(8:0000 1000    16:0001 0000    24:0001 1000)

注意:a必须是 Linux常用内核宏_宏定义

还有一个宏用于判断是否字节对齐

#define IS_ALIGNED(x, a)      ( ( (x) & ((typeof(x))(a) - 1) ) == 0)

参考​

强制编译出错

#define BUILD_BUG_ON(e)         (sizeof(char[1 - 2 * !!(e)])    )
#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)

 

注意核心表达式sizeof(char[1 - 2*!!(condition)])的作用,首先对条件表达式进行两次取反,这可以保证进行1 - 2*!!(condition)的结果只有两种值:条件为0时结果为1或者不为0则结果为-1,显然char[-1]将会导致编译器报错。注意:两次取反的目的是为了将表达式的值转换为逻辑值。

  • condition != 0:编译器报错
  • condition =  0:上面返回1   下面返回0

主要用在结构或其它自定义类型不满足指定大小就需要编译报错。

根据机构体成员地址找到结构体首地址

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })

这个大家应该都很熟悉了,我这里就不说了

如果不懂可以参考我的这篇博客,有详细的解释​

 

各个数据类型的最大值和最小值

#define USHRT_MAX ((u16)(~0U))
#define SHRT_MAX ((s16)(USHRT_MAX>>1))
#define SHRT_MIN ((s16)(-SHRT_MAX - 1))
#define INT_MAX ((int)(~0U>>1))
#define INT_MIN (-INT_MAX - 1)
#define UINT_MAX (~0U)
#define LONG_MAX ((long)(~0UL>>1))
#define LONG_MIN (-LONG_MAX - 1)
#define ULONG_MAX (~0UL)
#define LLONG_MAX ((long long)(~0ULL>>1))
#define LLONG_MIN (-LLONG_MAX - 1)
#define ULLONG_MAX (~0ULL)

va_list 、va_start、 va_arg、 va_end

当我们用可变参数来作为函数参数时,怎么一个一个的取得参数,如下

int DebugPrint(const char *pcFormat, ...) {

}

Glibc中有对应的宏,虽然这不是内核中的宏,但是还是可以拿来讲一下。

typedef char *va_list;

va_start宏,获取可变参数列表的第一个参数的地址(list是类型为va_list的指针,param1是可变参数最左边的参数):
#define va_start(list,param1) ( list = (va_list)&param1+ sizeof(param1) )

va_arg宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(mode参数描述了当前参数的类型):
#define va_arg(list,mode) ( (mode *) ( list += sizeof(mode) ) )[-1]

va_end宏,清空va_list可变参数列表:
#define va_end(list) ( list = (va_list)0 )

简单用法1

#include <stdio.h>
#include <stdarg.h>
void var_test(char *format, ...) {
va_list list;
va_start(list,format);
char *ch;
while(1) {
ch = va_arg(list, char *); //ch获得当前参数的地址,调用后,list指向下一个参数
if(strcmp(ch,"") == 0) {
printf("\n");
break;
}
printf("%s ",ch);
}
va_end(list);
}
int main() {
var_test("test","this","is","a","test","");
return 0;
}

简单用法2

#include<stdarg.h>
#include<stdio.h>
int sum(int num_args, ...) {
int val = 0;
va_list ap;
int i;
va_start(ap, num_args);
for(i = 0; i < num_args; i++) {
val += va_arg(ap, int);
}
va_end(ap);
return val;
}
int main(void) {
printf("10、20 和 30 的和 = %d\n", sum(3, 10, 20, 30) );
return 0;
}

简单用法3

int DebugPrint(const char *pcFormat, ...) {
va_list tArg;
/* 可变参数的处理, 抄自glibc的printf函数 */
va_start (tArg, pcFormat);
iNum = vsprintf (strTmpBuf, pcFormat, tArg);
va_end (tArg);
}