计算向向上取整的商
由于两数相除,默认是向下取整,而这里是向上取整数
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
参考:https://www.cnblogs.com/cmembd/p/3493597.html
计算向下取整的整数倍数
如: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为 ,所有 a - 1 再取反 后低位全为 0 高位全为1
举例:x = 5 a = 4(4 -->> 1111 1100)
由于高位不管是取0还是取1都是都是4的整数倍,如(8:0000 1000 16:0001 0000 24:0001 1000)
注意:a必须是
还有一个宏用于判断是否字节对齐
#define IS_ALIGNED(x, a) ( ( (x) & ((typeof(x))(a) - 1) ) == 0)
参考:https://www.cnblogs.com/3me-linux/p/6026914.html
强制编译出错
#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)); })
这个大家应该都很熟悉了,我这里就不说了
如果不懂可以参考我的这篇博客,有详细的解释:https://blog.csdn.net/qq_40732350/article/details/82960082
各个数据类型的最大值和最小值
#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)¶m1+ 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);
}