查看: 92|回复: 15
收起左侧

C语言中宏的用法探讨

[复制链接]

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:38 | 显示全部楼层 |返回版面|阅读模式
C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简单的字符串替换,根据是否带参数分为无参和带参。
(1)宏中包含特殊符号:#、##.
(2)宏定义用do{ }while(0)
这是两个最主要的特殊用法的。。

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:39 | 显示全部楼层 |返回版面
(1)# 

 在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组 

 简化理解:#是“字符串化”的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串,,如:

#define ERROR_LOG(module)   fprintf(stderr,"error: "#module"\n")
ERROR_LOG("add"); 转换为 fprintf(stderr,"error: "add"\n");

ERROR_LOG(devied =0); 转换为 fprintf(stderr,"error: devied=0\n");

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:40 | 显示全部楼层 |返回版面
(2)##

  “##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。

  在普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。

1 #define TYPE1(type,name)   type name_##type##_type
2 #define TYPE2(type,name)   type name##_##type##_type
TYPE1(int, c); 转换为:int  name_int_type ; (因为##号将后面分为 name_ 、type 、 _type三组,替换后强制连接)
TYPE2(int, d);转换为: int  d_int_type ; (因为##号将后面分为 name、_、type 、_type四组,替换后强制连接)

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:42 | 显示全部楼层 |返回版面
这一种目前用的还是比较少的,,先分隔在强制连接的用法主要起什么作用呢?

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:44 | 显示全部楼层 |返回版面
(3)宏定义中do{ }while(0)

   第一眼看到这样的宏时,觉得非常奇怪,为什么要用do……while(0)把宏定义的多条语句括起来?非常想知道这样定义宏的好处是什么,于是google、百度一下了。

    采用这种方式是为了防范在使用宏过程中出现错误,主要有如下几点:

  (1)空的宏定义避免warning:
  #define foo() do{}while(0)
  (2)存在一个独立的block,可以用来进行变量定义,进行比较复杂的实现。
  (3)如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:
      #define foo(x) \
        action1(); \
        action2();
    在以下情况下:
    if(NULL == pPointer)
        foo();
    就会出现action1和action2不会同时被执行的情况,而这显然不是程序设计的目的。

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:44 | 显示全部楼层 |返回版面
(4)以上的第3种情况用单独的{}也可以实现,但是为什么一定要一个do{}while(0)呢,看以下代码:
      #define switch(x,y) {int tmp; tmp="x";x=y;y=tmp;}
      if(x>y)
        switch(x,y);
      else       //error, parse error before else
      otheraction();
    在把宏引入代码中,会多出一个分号,从而会报错。这对这一点,可以将if和else语句用{}括起来,可以避免分号错误。
  使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低.。

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:48 | 显示全部楼层 |返回版面
在补充一点的哈。。。
对宏定义的几点说明

1) 宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的源程序时发现。

2) 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换。

3) 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:50 | 显示全部楼层 |返回版面
4)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名,在宏展开时由预处理程序层层代换。
5)习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

26

主题

1387

帖子

4165

积分

中级工程师

 楼主| 发表于 2017-11-24 20:51 | 显示全部楼层 |返回版面
补充重要的一点——
应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。

请看下面的例子:
#define PIN1 int *
typedef (int *) PIN2;
从形式上看这两者相似, 但在实际使用中却不相同。

下面用PIN1,PIN2说明变量时就可以看出它们的区别:
PIN1 a,b;
在宏代换后变成:
int *a,b;
表示a是指向整型的指针变量,而b是整型变量。然而:
PIN2 a,b;
表示a、b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型, 但毕竟是作字符代换。在使用时要分外小心,以避出错。
     

22

主题

900

帖子

2687

积分

初级工程师

发表于 2017-11-24 21:04 | 显示全部楼层 |返回版面
宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的替换。
     

22

主题

900

帖子

2687

积分

初级工程师

发表于 2017-11-24 21:05 | 显示全部楼层 |返回版面
这个在宏的定义使用中非常不错的,,,使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低.。

4

主题

51

帖子

153

积分

中级技术员

发表于 2017-11-26 15:11 | 显示全部楼层 |返回版面
宏用好了,程序可移植性好

25

主题

1025

帖子

3085

积分

中级工程师

发表于 2017-11-27 23:30 | 显示全部楼层 |返回版面
##的用法还没使用过的哈

5

主题

36

帖子

102

积分

中级技术员

发表于 2017-11-29 10:18 | 显示全部楼层 |返回版面
可以带参数啊

86

主题

3850

帖子

1万

积分

资深工程师

发表于 2017-11-30 19:34 | 显示全部楼层 |返回版面
baimiaocun2015 发表于 2017-11-24 20:40
(2)##

  “##”是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。

这种用法很少见的,,需要理解透彻的。
后来乍到,前辈们多多包涵了啊。。

68

主题

3690

帖子

1万

积分

资深工程师

发表于 2017-11-30 21:03 | 显示全部楼层 |返回版面
baimiaocun2015 发表于 2017-11-24 20:44
(3)宏定义中do{ }while(0)

   第一眼看到这样的宏时,觉得非常奇怪,为什么要用do……while(0)把宏定 ...

do...while的设计在宏设计中还是非常必要的
您需要登录后才可以回帖 登录 | 注册 手机登录

本版积分规则

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