C语言---比较常用的运算技巧

本文介绍几个常用的计算技巧实现某一目的

大部分来源于内核的实现

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的使用

  1. #ifdef xxx 用来判断该宏是否定义不管定义是啥
  2. #if 或者#elif 需要判断后面的表达式的值为1才进
    #define AAA 0
    #define BBB
    #if AAA 不进
    #elif BBB不进
    #else 进
    #endif
  3. 搭配使用
    #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"

扩展:

当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
  1. 非’#‘和’##'的情况
    #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).
  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)
实际上编译器会报错,
error: 'zhangshanshan' undeclared (first use in this function)

原因,这个没有当做一个字符串来看只是拼在一起了
这样修改
#define name STR(P(zhang,shan,shan))
切记:
#define name P(“zhang”,“shan”,“shan”)
这样也是会报错的,不要以为字符串拼起来就是字符串。##左右不能为字符串属性
error: pasting ""zhang"" and ""shan"" does not give a valid preprocessing token

扩展三:

#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("%% \ !")就可以

Guess you like

Origin blog.csdn.net/weixin_41884251/article/details/111610080