C语言宏定义"#","##"巧妙用法

C语言宏定义的巧妙用法

在我学习32的过程中发现了这样一段代码:

/*信息输出*/
#define EEPROM_DEBUG_ON         1

#define EEPROM_INFO(fmt,arg...)           printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
#define EEPROM_ERROR(fmt,arg...)          printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...)          do{\
                                          if(EEPROM_DEBUG_ON)\
                                          printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

这种代码是参考Linux内核写出来,方便我们在32使用过程中检测信息,返回错误,以及程序调试。我相信大部分人会发现,对于宏的了解绝对没有自己想像中的那么多。

下面就来讲讲宏:

C语言中的宏定义

#define是预处理器处理的单元实体之一
#define定义的可以出现在程序的任意位置
#define定义之后的代码都可以使用这个宏

C语言中的宏常量
#define定义的宏常量可以直接使用
#define定义的宏常量本质为字面量

1、宏可以像大多数函数一样被定义,我记得某公司出过这样一道面试题,要求用宏定义来比较大小,输出最大值。
这道题可能看似很简单我们只需要定义一个宏和两个参数就可以了。

像这样:#define MAX(x,y) x > y ? x : y
但要是只能写出这样的代码只能说明我们有相应的基础,但这还远远不够。
要知道预处理器遇到宏定义时原则上会全部展开,这样运行上面的代码似乎没有什么问题。

但当我们运行下例代码时:printf("max=%d",MAX(1!=1,1!=2));我们会发现输出的结果跟我们的预期是不一样的,这是因为printf();函数展开后是这样的:printf("max=%d",1!=1>1!=2?1!=1:1!=2);
我们可以将宏改为#define MAX(x,y) (x) > (y) ? (x) : (y)到此这才算是一个可以运行的宏;
但此宏还是有问题,本文主要讲解“#”和“##”用法。
想要了解如何更好的写出近乎完美的宏定义可以参考:C语言仅凭自学能到什么高度?.

2、当宏自己调用自己时会发生什么:
例:#define ADD(x) (ADD(x)+x)
如上宏定义调用时会直接展开,我们可能会认为当前宏会对自己无限递归。但C语言为了防止无限递归造成死循环。语法规定,当宏在遇到自己时,就停止展开当前宏,也就是说,ADD(1)的结果为:“ADD(1)+1”

3、在我使用中经常用到的宏:条件编译
先让我们来看一段代码:

#ifndef __I2C_EE_H
#define	__I2C_EE_H
#include "stm32f10x.h"

/**************************I2C参数定义,I2C1或I2C2********************************/
#define             EEPROM_I2Cx                                I2C1
#define             EEPROM_I2C_APBxClock_FUN                   RCC_APB1PeriphClockCmd
#define             EEPROM_I2C_CLK                             RCC_APB1Periph_I2C1
#define             EEPROM_I2C_GPIO_APBxClock_FUN              RCC_APB2PeriphClockCmd
#define             EEPROM_I2C_GPIO_CLK                        RCC_APB2Periph_GPIOB     
#define             EEPROM_I2C_SCL_PORT                        GPIOB   
#define             EEPROM_I2C_SCL_PIN                         GPIO_Pin_6
#define             EEPROM_I2C_SDA_PORT                        GPIOB 
#define             EEPROM_I2C_SDA_PIN                         GPIO_Pin_7
#endif /* __I2C_EE_H */

这基本上是在我创建用户函数时一定会用到它。因为,条件编译可以解决头文件重复编译的错误。
#if... #else #endif则可以让我们有选择的去执行我们想要其执行的代码段。

4、我们不曾知道的“#”,“##”。
“#”符号可以把一个符号直接转换为字符串。
例:#define STRING(x) #x
char * str = STRING( string );
这段的意思就是str中的内容是字符串string。
而“##”则会连接临近的符号从而产生新的符号。
例:#define CONS (x,y) (x**e**y)
printf("CONS IS %d\n",CONS(2,3));
因为“##”会将相近的符号连接起来,所以这段代码则会输出 “CONS IS 2000”;

5、当宏参数是另一个宏时会发生什么?
宏参数是另外一个宏时,会先展开作为参数的宏,当展开的宏参数被放进宏体时,预处理器将会对新展开的宏体进行展开。
例:#define A(x) x #define CONS (x,y) (x**e**y) A(CONS(2,3));2和3作为宏CONS的参数对CONS进行展开为2000,再将展开的结果作为参数放入A(x)。

例外情况:当宏主体对参数使用了“#”或“##”时,宏将不会对参数进行展开。
例:
#define TOSRTING(x) #x #define STRING(x) TOSRTING(x)
#define A(x) #x #define CONS (x,y) (x**e**y)
char * str = STRING(A(CONS(2,3)));可以试想一下str中存储的字符串为多少。

6、变参宏,可以让你定义类似的宏:
#define EEPROM_ERROR(format, ... ) printf("<<-EEPROM-INFO->> "format"\n",__VA_ARGS__)
EEPROM_ERROR("I2C 等待超时!errorCode = %d",errorCode);
__VA_ARGS__为系统预定义宏,会被自动替换为参数列表。

在看完上述文章后:我想大家应该对宏定义有一个比较深刻的认识了,再回来看这段代码:

/*信息输出*/
#define EEPROM_DEBUG_ON         1

#define EEPROM_INFO(fmt,arg...)           printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
//EEPROM_ERROR("I2C 等待超时!errorCode = %d",errorCode);如何调用
#define EEPROM_ERROR(fmt,arg...)          printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...)          do{\
                                          if(EEPROM_DEBUG_ON)\
                                          printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)

相信大家可以很容易的知道,printf函数中各参数的意思了。

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值