常用一些东西
本文介绍几个常用的计算技巧实现某一目的
大部分来源于内核的实现
1、小计算宏
1. #define OFFSET(type, member) (size_t)&( ((type*)0)->member)
2. #define ROUND_UP(x, align) (((x) + ((align) - 1)) &~ ((align) - 1)) //对齐计算向上对齐,
3. #define ROUND_DOWN(x, align) (((x) / (align)) * (align)) //对齐计算向下对齐
4. #define swap(a, b) \
do {
typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
OFFSET 计算一个结构体类型中成员变量的偏移
ROUND_UP x按align对齐返回(3, 4)返回的是4,(7,4)返回8
swap 交换a和b的值
2、container_of 内核常用宏
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({
\
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
根据成员变量的指针反推这个结构体的指针
ptr指向成员变量(成员变量指针),type结构体类型,member这个成员ptr变量在结构体中的名字
3、fifo
#define WR_MARGIN (w + bufsize - r)% bufsize
#define BEFOR_ONE (w + bufsize -1)% bufsize
fifo中有读写指针,该宏计算读写指针的差值防溢出
fifi w指针的前一个,防止w为0溢出取余并不一定等于数组的最后一个的情况
计算温度
int temp_start_conv(uint16_t temp_val)
{
uint32_t temp = 0;
uint16_t temp_h,temp_l;
int temp_ret = 0;
//一次读16位 的温度传感器数据 输出 温度低8位在前高位在后, 得到是数据的高8位其实对于温度寄存器的低8位
//eg:读到的值0x20c5 实际温度0xc52转化温度
temp_h = temp_val & 0xff;
//本配置为12bit有效数据(bit15--bit4)且低4位为0,
//temp_val >> 12 为获得低8位的数据
temp_l = temp_val >> 12;
//temp_h的8位+temp_l的4位组成12bit的温度量化值
temp_val = (temp_h << 4) + temp_l;
//判断是否为负温度
if (temp_val & 0x800)
{
temp_val = temp_val | 0xf000; //使高位保持1,表示负数
temp = (~temp_val & 0x0fff) + 1; //只需取低12位,当前平台取反自动扩大到32位值为ffff0xxx
}
else
{
temp = temp_val;
}
//通过量化值计算实际温度值刻度为(0.0625℃/LSB)
temp_ret = temp * 625;
if (temp_val & 0x800)
{
temp_ret = ~(temp_ret - 1);
}
return temp_ret;
}
测试平台是否支持该系统调用
int main(void)
{
int fd = -1;
fd = epoll_create1 (EPOLL_CLOEXEC);
if(fd < 0) {
printf("ret:%d,%d[%s]\n", fd, errno, strerror(errno));
perror("abd22");
}
printf("ret:%d\n", fd);
while(1)
{
sleep(1);
}
return 0;
}
#if、#if defined的使用
- #ifdef xxx 用来判断该宏是否定义不管定义是啥
- #if 或者#elif 需要判断后面的表达式的值为1才进
#define AAA 0
#define BBB
#if AAA 不进
#elif BBB不进
#else 进
#endif - 搭配使用
#if (defined (AAA) || defined (BBB)) 进
#else
#endif
编译
在编译脚本中可以通过cflags传一些宏定义进去
export CFLAGS="-DAAA_$1 -D_CUR_PLT_CPU=$1"
make XXX
同样在makefile中可以
gcc -D _CUR_PLT=F103
-D macro=string,等价于在头文件中定义:#define macro string
-DAAA_$1 相当于#define AAA_$1
在代码中就可以
#ifdef AAA_333 假如$1 == 333
XXXX
#elif defined _CUR_PLT
printf("%s",_CUR_PLT);
#endif
#和##用法
#define MCU_PLT(plt, cpu) plt##cpu
MCU_PLT(stm32,f103) ->stm32f103
#define PLT_FORM MCU_PLT(stm32,f103)
#define STR(a) #a
#define MP(a,b) printf(#a'-'#b"\n")
printf("%s",STR(1=1))= > "1=1"
MP(1+1,5ad) => "1+1-5ad\n"
扩展:
当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
- 非’#‘和’##'的情况
#define TOW(2)
#define MUL(a,b) (ab)
printf("%d%d=%d\n", TOW, TOW, MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2). - 当有’#‘或’##'的时候
#define A(2)
#define CONS(a,b)int(a##e##b)
这行会被展开为:
printf(“%s\n”, CONS(A, A));// compile error
这一行则是:printf("%s\n", int(AeA));
INT_MAX和A都不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏. 加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.
#define A(2)
#define _CONS(a,b)int(a##e##b)
#define CONS(a,b)_CONS(a,b)// 转换宏
printf("%d\n", CONS(A, A));
输出为:200
CONS(A, A)–>_CONS((2), (2))–> int((2)e(2))
扩展二:
#define STR(a) #a
#define P(a,b,c) a##b##c
#define name P(zhang,shan,shan)
实际上编译器会报错,
原因,这个没有当做一个字符串来看只是拼在一起了
这样修改
#define name STR(P(zhang,shan,shan))
切记:
#define name P(“zhang”,“shan”,“shan”)
这样也是会报错的,不要以为字符串拼起来就是字符串。##左右不能为字符串属性
扩展三:
#include <stdio.h>
#define STR(X) #X
#define AA(X) X
#define aaa 123
int main(void)
{
printf("AKK %s\n",STR(ddd)); //AKK ddd
printf("AKK %s\n",STR(aaa)); //AKK aaa
printf("AKK %s\n",STR(AA(aaa)));//AKK AA(aaa)
}
看了这个例子就明白了,#xx的绝对简单粗暴,也不展开后面的宏。
那如果我想要打印123怎么操作,和之前一样用二级宏,
#define STR(X) #X
#define SSTR(X) STR(X)
#define aaa 123
#define bbb (456)
#define ccc aaa
#define ddd ccc
int main(void)
{
printf("AKK %s\n",SSTR(aaa)); //AKK 123
printf("AKK %s\n",STR(aaa)); //AKK aaa
printf("AKK %s\n",SSTR(bbb)); //AKK (456)
printf("AKK %s\n",SSTR(ccc)); //AKK 123
printf("AKK %s\n",SSTR(ddd)); //AKK 123
//再多级也都执行正确,后面的都会逐级展开
}
注意bbb的括号定义的时候不能有 不然就会打出来
扩展四
##的使用拼接中不能出现有意义的符号比如+ - * ,以及字符串""”等
#define P(a,b,c) a##b##c
#define name P(zhang,shan,shan)
P(zhang,-,shan)//baocuo
//error: pasting "zhang" and "-" does not give a valid preprocessing token
P(zhang,_,shan)//但是可以用下划线
非要加-的话
#define name(a,b) a"-"b
SSTR(name(zhang,shan))//定义在宏里并采用字符串
高阶
#define _STR(str) #str
#define STR(str) _STR(str)
#define __BLD_NAME(IF, CPU) "u-boot_"IF##_##CPU".bin"
#define __PLT_NAME(IF, CPU) IF'-'CPU
#define NAME_BLD(IF, CPU) __BLD_NAME(IF, CPU)
#define NAME_PLT(IF, CPU) __PLT_NAME(IF, CPU)
#define CUR_IF net
#define CPU arm9
NAME_PLT(CUR_IF ,CPU ) --> net'-'arm9
NAME_BLD(CUR_IF ,CPU) --> "u-boot_"net_arm9".bin"
--------------------------------------------------------------
必须在__BLD_NAME中定义-
以及在__PLT_NAME 定义- 不能采用##拼接,-是拼不起来的会报错
假如结果不想要" 在宏定义的时候去掉符号即可,带上的话同样会传导下面去的
#define __BLD_NAME(IF, CPU) u-boot_##IF##_##CPU.bin
#define __PLT_NAME(IF, CPU) IF-CPU
这里要注意_后面必须加##IF否者_IF会被认为连着的编译器区分不出来
假如定义u-boot_IF##_##CPU.bin会变成u-boot_IF_arm9.bin
编译器会是被CPU后面的.从而吧CPU赋值,假如写成##_##CPU##.bin
反而编译报错,和哪个- + * " 一样的错误##两边必须是无意义的。
----------------------------------------------------
以下写法用-是错误的:
#define ___NAME(IF,SAMP,CPU) IF##SAMP##CPU
#define NAME(IF,SAMP,CPU) ___NAME(IF,SAMP,CPU)
NAME(CUR_IF,-, CPU ) 错的
NAME(CUR_IF,"-", CPU ) 也是错的
NAME(CUR_IF,"_", CPU ) 也是错的
NAME(CUR_IF,_, CPU ) 不报错,注意结果不是字符串没有"", --> net_arm9
NAME(u-boot_net,_,CPU)不报错 -->u-boot_net_arm9
注意
NAME("u-boot_net",_,CPU)//这样就会报错##两边不能出现""
""u-boot_net"" and "_" does not give a valid preprocessing token
#define __BLD_NAME(IF, CPU) "u-boot_"IF##_##CPU".bin"
包括这里也不能写成"u-boot_"##IF##_##CPU##".bin"的形式,这样是不合法的
strerror
printf(“vcap init fail=%d %s\n”, ret, strerror(errno));
printf
在C语言中假如想打印% \ 这种符号 两个就可以比如想答应‘%’ printf("%% \ !")就可以