注册 登录
查看: 2726|回复: 18

关于宏定义的用法

[复制链接]
发表于 2013-5-13 18:34:41 | 显示全部楼层 |阅读模式
Q群中总会有人会问各种不同的问题,而很多问题很适合其他人参考了,故把它粘贴上来,以供参考:



如何解释下面这段代码:
#define LED1(a)   if (a)\
                         GPIO_SetBits(GPIOC,GPIO_Pin_3);\
                    else \
                         GPIO_ResetBits(GPIOC,GPIO_Pin_3)

首先,这个是用宏定义的方式包装成类似函数那样,但不是函数调用
你在代码中调用:
        LED1(1);
实际上通过宏定义替换,代码会替换成:
        if (1) GPIO_SetBits(GPIOC,GPIO_Pin_3); elseGPIO_ResetBits(GPIOC,GPIO_Pin_3)
宏定义中的 a 就被 调用时 的 ‘1’ 所替换掉,就类似函数那样,但不是函数。

另外,有没有发现替换后的代码只有一行,而不是宏定义中的多行呢?
回答这个问题,你就必须先了解 c语言 中 反斜杠( \)的作用:语义上表示,下一行是上一行的延续。也就是同一行。
当你的代码一行写的时候会太长,需要分行方便显示时,但代码又不能分行时,例如这里的宏定义,只能在一行定义好,那样就可以用过在结尾添加 反斜杠( \) 来换行。表示 接着下一行,就是例子中的整个 if-else 语句都被 反斜杠( \) 连接在同一行,所以替换后就仅仅一行而已。

注意,反斜杠( \) 后面不能有任何字符,包括空格。


宏定义的用法

注意:宏定义不是函数!!

一般用来简化操作的,但又能避免函数调用那样需要进行切换环境,花费时间。例如:
#define max (a,b) (a>b?a:b)
#define MALLOC(n, type)   ((type *)  malloc( (n) * sizeof (type) ))

使用时,我只需:
a=max (a,b);                 //而不是a=(a>b?a:b);
int *p=MALLOC(10,int);  //而不是int *p= ((int *)  malloc( (10) * sizeof (int) ))

网上copy一篇不知出自哪里的文章:
1、防止一个头文件被重复包含
#ifndef COMDEF_H
#define COMDEF_H  //头文件内容
#endif

2、重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。
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 */

3、得到指定地址上的一个字节或字
#define  MEM_B( x )  ( *( (byte *) (x) ) )
#define  MEM_W( x )  ( *( (word *) (x) ) )

4、求最大值和最小值
   #define  MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )
   #define  MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )

5、得到一个field在结构体(struct)中的偏移量
#define FPOS( type, field )   ( (dword) &(( type *) 0)-> field )

6、得到一个结构体中field所占用的字节数
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )

7、按照LSB格式把两个字节转化为一个Word
#define  FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] )

8、按照LSB格式把一个Word转化为两个字节
#define  FLOPW( ray, val ) \
  (ray)[0] = ((val) / 256); \
  (ray)[1] = ((val) & 0xFF)

9、得到一个变量的地址(word宽度)
#define  B_PTR( var )  ( (byte *) (void *) &(var) )
#define  W_PTR( var )  ( (word *) (void *) &(var) )

10、得到一个字的高位和低位字节
#define  WORD_LO(xxx)  ((byte) ((word)(xxx) & 255))
#define  WORD_HI(xxx)  ((byte) ((word)(xxx) >> 8))

11、返回一个比X大的最接近的8的倍数
#define RND8( x )       ((((x) + 7) / 8 ) * 8 )

12、将一个字母转换为大写
#define  UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) )

13、判断字符是不是10进值的数字
#define  DECCHK( c ) ((c) >= '0' && (c) <= '9')

14、判断字符是不是16进值的数字
#define  HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\
                       ((c) >= 'A' && (c) <= 'F') ||\
((c) >= 'a' && (c) <= 'f') )

15、防止溢出的一个方法
#define  INC_SAT( val )  (val = ((val)+1 > (val)) ? (val)+1 : (val))

16、返回数组元素的个数
#define  ARR_SIZE( a )  ( sizeof( (a) ) / sizeof( (a[0]) ) )

17、返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
#define MOD_BY_POWER_OF_TWO( val, mod_by ) \
           ( (dword)(val) & (dword)((mod_by)-1) )

18、对于IO空间映射在存储空间的结构,输入输出处理
  #define inp(port)         (*((volatile byte *) (port)))
  #define inpw(port)        (*((volatile word *) (port)))
  #define inpdw(port)       (*((volatile dword *)(port)))
   
  #define outp(port, val)   (*((volatile byte *) (port)) = ((byte) (val)))
  #define outpw(port, val)  (*((volatile word *) (port)) = ((word) (val)))
  #define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

19、使用一些宏跟踪调试
A N S I标准说明了五个预定义的宏名。它们是:
_ L I N E _
_ F I L E _
_ D A T E _
_ T I M E _
_ S T D C _
如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序
也许还提供其它预定义的宏名。
_ L I N E _及_ F I L E _宏指令在有关# l i n e的部分中已讨论,这里讨论其余的宏名。
_ D AT E _宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
源代码翻译到目标代码的时间作为串包含在_ T I M E _中。串形式为时:分:秒。
如果实现是标准的,则宏_ S T D C _含有十进制常量1。如果它含有任何其它数,则实现是
非标准的。
可以定义宏,例如:
当定义了_DEBUG,输出数据信息和所在文件所在行
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
#else
      #define DEBUGMSG(msg,date)  
#endif

20、宏定义防止 使用是错误
用小括号包含。
例如:#define ADD(a,b) (a+b)
用do{}while(0)语句包含多语句防止错误
例如:#difne DO(a,b) a+b;\
                   a++;
应用时:if(….)
                    DO(a,b); //产生错误
            else
                   ……
解决方法: #difne DO(a,b) do{a+b;\
                   a++;}while(0)


本帖被以下淘专辑推荐:

回复

使用道具 举报

发表于 2013-7-30 17:16:00 | 显示全部楼层
QQ群加了没反应
回复 支持 反对

使用道具 举报

发表于 2013-7-31 13:51:01 | 显示全部楼层
留名,有些看不懂,挺有用
回复 支持 反对

使用道具 举报

发表于 2013-7-31 14:06:05 | 显示全部楼层
做个慢跑者 发表于 2013-7-31 13:51
留名,有些看不懂,挺有用

看不懂就百度,或者问下,不要把疑问留给后面
回复 支持 反对

使用道具 举报

发表于 2013-8-1 17:24:07 | 显示全部楼层
太棒了,非常感谢

喜欢山外哥的帖子,讲的详细啊
回复 支持 反对

使用道具 举报

发表于 2013-8-2 10:08:01 | 显示全部楼层
真棒!!非常感谢
回复 支持 反对

使用道具 举报

发表于 2013-8-16 08:57:50 | 显示全部楼层
学习了,不过还是有问题不太懂
回复 支持 反对

使用道具 举报

发表于 2013-9-26 21:53:03 | 显示全部楼层
真棒!!非常感谢
回复 支持 反对

使用道具 举报

发表于 2014-1-7 15:40:40 | 显示全部楼层
#define LED1(a)   if (a)\
                         GPIO_SetBits(GPIOC,GPIO_Pin_3);\
                    else \
                         GPIO_ResetBits(GPIOC,GPIO_Pin_3)

那这一段中,  把\去掉不要。。。其他不变。

#define LED1(a)   if (a)
                         GPIO_SetBits(GPIOC,GPIO_Pin_3);
                    else
                         GPIO_ResetBits(GPIOC,GPIO_Pin_3)

就这样写,不可以吗?

我看51当中  一般人写的
if
else
这两个都是独立一行。也没见到加\的   

新手初学。见谅!

点评

当然不可以,这是宏定义,不是普通写程序,宏定义后面的结构只能在同一行。  发表于 2014-8-22 20:08
回复 支持 反对

使用道具 举报

发表于 2014-1-7 15:42:54 | 显示全部楼层
sn516952 发表于 2014-1-7 15:40
#define LED1(a)   if (a)\
                         GPIO_SetBits(GPIOC,GPIO_Pin_3);\
               ...

不可以,51 单片机 用到的 知识点 非常少!!很多语法都没用过的。

这个是 宏定义,宏定义 要求 一行写完。但如果需要分开多行写,就需要续行符 '\',而且 后面不能加任何东西,包括空格
回复 支持 反对

使用道具 举报

发表于 2014-1-7 15:48:12 | 显示全部楼层
说不得 发表于 2014-1-7 15:42
不可以,51 单片机 用到的 知识点 非常少!!很多语法都没用过的。

这个是 宏定义,宏定义 要求 一行写 ...

哦。原来是这样。。。那我必须把这个当1+1=2一样记住了。。
回复 支持 反对

使用道具 举报

发表于 2014-3-7 08:15:47 | 显示全部楼层
很有用,赞一个!
回复 支持 反对

使用道具 举报

发表于 2014-4-2 15:06:28 | 显示全部楼层
顶一个,这个比较有用的
回复 支持 反对

使用道具 举报

发表于 2014-5-22 20:07:02 | 显示全部楼层
还是不太明白,if else 不能加{}吗?为什么?
回复 支持 反对

使用道具 举报

发表于 2014-8-13 16:03:41 | 显示全部楼层
受教了!感谢分享!
回复 支持 反对

使用道具 举报

发表于 2014-11-14 13:15:34 | 显示全部楼层
懂一些
略懂
回复 支持 反对

使用道具 举报

发表于 2015-9-21 20:22:17 | 显示全部楼层

回复 支持 反对

使用道具 举报

发表于 2015-10-24 20:47:44 | 显示全部楼层
学习了
回复 支持 反对

使用道具 举报

发表于 2015-12-19 22:17:19 | 显示全部楼层
受教
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回列表 返回顶部