宏定义

1、重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植,假如平台不一样,字节不一样,就仅仅改变typedef定义即可。

typedef  unsigned char      boolean;     /* Boolean value type. */
typedef  unsigned long int   uint32;      /* Unsigned 32 bit value */
typedef  unsigned short     uint16;      /* Unsigned 16 bit value */
typedef  unsigned char      uint8;       /* Unsigned 8  bit value */
typedef  signed long int     int32;       /* Signed 32 bit value */
typedef  signed short       int16;       /* Signed 16 bit value */
typedef  signed char        int8;        /* Signed 8  bit value */

2、防止头文件被重复包含

#ifndef __AD2S1210_H__
#define __AD2S1210_H__
#endif

3、已知一个结构体成员的地址,求结构体首地址

#define offsetof(TYPE, MEMBER) ((unsigned int) &((TYPE *)0)->MEMBER)
//通过0地址得到结构体中成员地址的偏移量也就是相对于0这个地址的数值。

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

//1、offsetof是偏移的数值就是距离首地址有多少字节。
//指针类型加减数值相当于当前指针加减(指针所指类型字节*数值)。
//为了不影响ptr的类型,所以通过缓存__mptr来处理。 


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

 void fun(int a , int b)
 {
     printf("test");
 }
 int main(void)
 {
     struct str{
         int a;
         char b;
         int c;
         int d;
         void (*fun)(int ,int);
     }str;
     str.a = 12;
     str.b = 24;
     str.c = 24;
     str.d = 24;
     str.fun = fun;
     printf("0x%x\n" , &str);
     printf("0x%x\n" , container_of(&str.fun , struct str , fun));
     exit(0);
 }
0xa089aa10
0xa089aa10
按 <RETURN> 来关闭窗口...
//搞定
/*
编译出现错误error: cast from 'void (**)(int, int)' to 'unsigned int' loses precision [-fpermissive]
      printf("0x%x\n" , container_of(&str.fun , struct str , fun));                                                            
编译的系统为linux64位,其指针类型和long型大小相等(8字节)而与int型4B所以出现了精度丢失,系统报错。将unsigned int 改成unsigned long即可
*/

4、宏定义中do{ }while(0)

如果要使用宏定义来定义多条语句时,采用do { … } while (0) 的形式是一种较好的方法。空的宏定义避免warning;存在一个独立的block中,可以用来进行变量定义作用域是块,因此可以实现比较复杂的功能;如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现。

5、宏定义中#和##的用法

宏定义默认遇到换行符才终止,但是可以通过\续行。

  • #的功能是将其后面的宏参数进行字符串化操作(Stringizing),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号,然后利用输出函数时候可以直接输出字符。
#define WARNING(EXP)        \
        do{ if (EXP)        \
                fprintf(stderr, "Warning: " #EXP "\n"); } \
        while(0)

WARNING(divider == 0);//由于do{}while(0)的效果,后面可以加分号,类似函数调用。
//等效于:
do {
        if (divider == 0)
            fprintf(stderr, "Warning" "divider == 0" "\n");
} while(0);
  • ##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。
#define COMMAND(NAME) { NAME, NAME##_command }
struct command
{
char * name;
void (*function) (void);
};
struct command commands[] = {
COMMAND(quit),
COMMAND(help),
};//定义结构体数组并且通过宏定义初始化,前面应该定义了对应的quit_command()函数才可以。
#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d
LINK_MULTIPLE(name,company,position,salary);
//等效于name_company_position_salary;
//UBOOT里面运用#和##定义菜单宏。
struct cmd_tbl_s {
    char        *name;      /* Command Name         */
    int     maxargs;    /* maximum number of arguments  */
    int     repeatable; /* autorepeat allowed?      */
                    /* Implementation function  */
    int     (*cmd)(struct cmd_tbl_s *, int, int, char *[]);//function pointer
    char        *usage;     /* Usage message    (short) */
    char        *help;      /* Help  message    (long)  */

};
//这是结构体,里面存放菜单需要的函数指针和字符指针说明用法。
//为了达到直观的效果,利用宏定义进行初始化。
#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))
//链接时候放在对应的段,后期直接到指定地方寻址,快速。
typedef struct cmd_tbl_s    cmd_tbl_t;
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage} 
//定义一个变量,并进行初始化。运用了格式字符串和连接符。

U_BOOT_CMD(
    menu,   3,  0,  do_menu,
    "menu - display a menu, to select the items to do something\n",
    " - display a menu, to select the items to do something"
);//效果很实用,达到方便快捷的效果。

6、变参数宏:…和__VA_ARGS__

这里写图片描述

7、Side Effect是指宏在展开的时候对其参数可能进行多次Evaluation(也就是取值)

#define min(X,Y) ((X) > (Y) ? (Y) : (X))
int c = min(a,func(b));

//这时进行宏替换的时候func()函数就被调用了两次。很有可能出现问题,为了解决这个潜在的问题,改写宏定义min(X,Y):

#define min(X,Y) ({ \
      typeof (X) x_ = (X); \
      typeof (Y) y_ = (Y); \
      (x_ < y_) ? x_ : y_; }) //typeof将X类型返回用来定义x_暂存(X)。
//({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope),然后利用变量进行计算,最后一句是宏定义结果的返回。前面container_of宏定义用到了这种功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有时需要偏执狂

请我喝咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值