1、前言

    今天看代码时候,遇到一些宏,之前没有见过,感觉挺新鲜。如是上网google一下,顺便总结一下,方便以后学习和运用。C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简单的字符串替换,根据是否带参数分为无参和带参。宏的简单应用很容易掌握,今天主要总结一下宏的特殊符号及惯用法。

  (1)宏中包含特殊符号:#、##.

      (2)宏定义用do{ }while(0)

2、特殊符号#、##

(1)#

 When you put a # before an argument in a preprocessor  macro, the preprocessor turns that argument into a character array. 

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

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

#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");

(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四组,替换后强制连接)

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不会同时被执行的情况,而这显然不是程序设计的目的。
  (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)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低

4、测试程序

  简单写个测试程序,加强练习,熟悉一下宏的高级用法。

 1 #include <stdio.h>
2
3 #define PRINT1(a,b) \
4 { \
5 printf("print a\n"); \
6 printf("print b\n"); \
7 }
8
9 #define PRINT2(a, b) \
10 do{ \
11 printf("print a\n"); \
12 printf("print b\n"); \
13 }while(0)
14
15 #define PRINT(a) \
16 do{\
17 printf("%s: %d\n",#a,a);\
18 printf("%d: %d\n",a,a);\
19 }while(0)
20
21 #define TYPE1(type,name) type name_##type##_type
22 #define TYPE2(type,name) type name##_##type##_type
23
24 #define ERROR_LOG(module) fprintf(stderr,"error: "#module"\n")
25
26 main()
27 {
28 int a = 20;
29 int b = 19;
30 TYPE1(int, c);
31 ERROR_LOG("add");
32 name_int_type = a;
33 TYPE2(int, d);
34 d_int_type = a;
35
36 PRINT(a);
37 if (a > b)
38 {
39 PRINT1(a, b);
40 }
41 else
42 {
43 PRINT2(a, b);
44 }
45 return 0;
46 }

测试结果如下:

5、参考网址

http://blog.csdn.net/jiangjingui2011/article/details/6706967

http://www.kuqin.com/language/20080721/11906.html

http://www.360doc.com/content/12/0405/16/8302596_201146109.shtml

http://blog.csdn.net/liliangbao/article/details/4163440

【转】C语言宏高级用法 [总结]的更多相关文章

  1. C语言宏高级用法 [总结]

    1.前言  今天看代码时候,遇到一些宏,之前没有见过,感觉挺新鲜.如是上网google一下,顺便总结一下,方便以后学习和运用.C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简 ...

  2. C语言宏高级用法

    1.前言  今天看代码时候,遇到一些宏,之前没有见过,感觉挺新鲜.如是上网google一下,顺便总结一下,方便以后学习和运用.C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简 ...

  3. linux c语言 select函数用法

    linux c语言 select函数用法 表头文件 #i nclude<sys/time.h> #i nclude<sys/types.h> #i nclude<unis ...

  4. jsoup 使用总结4--高级用法之 script js 脚本

    jsoup 使用总结4--高级用法之 script js 脚本 大部分时候,我们使用jsoup解析网页的时候,都是直接找到某一类元素,或者按某种selector查询:具体使用方法可以参考jsoup官网 ...

  5. jsoup 使用总结3--高级用法之 not

    jsoup 使用总结3--高级用法之 not 大部分时候,我们使用jsoup解析网页的时候,都是直接找到某一类元素,或者按某种selector查询:具体使用方法可以参考jsoup官网文档 例子代码: ...

  6. jsoup 使用总结2--高级用法之 :gt(n)

    jsoup 使用总结2--高级用法之 :gt(n) 大部分时候,我们使用jsoup解析网页的时候,都是直接找到某一类元素,或者按某种selector查询:具体使用方法可以参考jsoup官网文档 部分h ...

  7. 9.11 Django关于母版语言的灵活用法

    2018-9-11 12:45:38 还是这个连接 : http://www.cnblogs.com/liwenzhou/p/7931828.html 注意: 这节讲的是  母版语言的灵活用法!  可 ...

  8. 单片机的C语言中位操作用法2

    单片机的C语言中位操作用法 在对单处机进行编程的过程中,对位的操作是经常遇到的.C51对位的操控能力是非常强大 的.从这一点上,就可以看出C不光具有高级语言的灵活性,又有低级语言贴近硬件的特点. 这也 ...

  9. C语言typedef的用法(转)

    http://www.cnblogs.com/afarmer/archive/2011/05/05/2038201.html 一.基本概念剖析 int* (*a[5])(int, char*);    ...

随机推荐

  1. firefox event.preventDefault(); 没有效果的解决方案

    $('.sub-list-click a').click(function (event) { event.preventDefault(); var sub = $(this).parent(&qu ...

  2. el-table表格标题换行

    在做一些管理后台的项目中,表格时最常见的,当有时数据字段多时往往会用滚动条,但从用户体验角度讲,肯定需要多展示信息,那么可能需要一个单元格放多个字段,这时候表头就需要换行. 具体实现如下: // 注意 ...

  3. css 位置居中篇,flex布局【转】

    最近看到沅老师的博客,关于flex布局的,觉得不错,http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

  4. 算法与cpu

    cpu中的控制单元对应算法中的控制: cpu重的计算单元对应算法中的顺序计算:

  5. 关于Hamilton问题的研究

    关于Hamilton问题的研究 首先介绍一下Hamilton问题:哈密顿问题寻找一条从给定的起点到给定的终点沿途恰好经过所有其他结点一次的路径.(摘自百度百科) 从刚开始学OI买了信息学一本通,这个问 ...

  6. 全排列(dfs)

    无重复元素的全排列 输入n(<=11),按从小到大输出数字1 到n 个的全部排列.样例:输入:3输出:1:1 2 32:1 3 23:2 1 34:2 3 15:3 1 26:3 2 1 全排列 ...

  7. 转载 【.NET基础】--委托、事件、线程(3)

      之前的两篇文章我们了解了委托和事件,本文我们看一下线程. 1,一个窗体程序,默认拥有一个线程(相当于一个商店里面,只有一个店员),这个默认的线程叫做 UI线程/主线程. 2,进程和线程的关系: A ...

  8. WordPress研究心得

    WordPress,当初之所以研究这个,目的是在于开发一个多租户系统应用.总的来说,WordPress给了我不少启发和影响.为此,我决定这个研究心得分享出去. 总的来说,这篇文章参考了很多朋友们的博客 ...

  9. mac修改本机mysql的root密码

    今天同事的MAC上的mysql的登陆密码忘记了,问我是否能解决,呵呵   我查了下    并做个记录 1. 在系统偏好设置中关闭 mysql : Stop MySQL Server 2.打开终端 进入 ...

  10. ubuntu (14.04) 卸载 gnome 系统桌面

    1.将ubuntu 的图形界面切到命令行界面. 2.卸掉 gnome-shell 的主程序 sudo apt-get remove gnome-shell 3.卸载 gnome sudo apt-ge ...