【转】C语言宏高级用法 [总结]

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

时间: 2024-08-27 19:43:39

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

关于闭包的终级用法

最近艾伦在研究jQuery的sizzle选择器,和我分享了一个sizzle里边关于闭包的高级用法,说它高级,是因为它用的特别巧妙,代码我们都能看明白,但是不一定能想到要这样去用闭包.然后他得意地笑了,然后说道:”现在知道为什么我坚持要看源码了吧,这样的用法,看明白了,就是你的知识积累“.不得不承认,艾伦确实是一个比较肯钻研的人.sizzle里边的闭包用法,我已经记不清了,但是那个思路我还是记下来,后面会附上自己理解后的模拟代码. code 1: function createClure(){ r

R语言seq()函数用法

1.seq() 用来生成一组数字的函数. Usage: ## Default S3 method:seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)),length.out = NULL, along.with = NULL, ...)seq.int(from, to, by, length.out, along.with, ...)seq_along(along.with)seq_len(length.out) Arguments:

单片机学习之:C语言基础——if 用法

//========if 用法(一)========= //*用法格式: if (表达式)    语句; //if判断语句,若表达式为真,则执行语句,否则不执行.且if仅能控制一条语句. ×××××××××××××××××××××××××××××××××× //==========if 用法(二)=============//用法格式: if (表达式)    {        语句1;        语句2;        语句N;    }  //if判断语句,若表达式为真,则执行大括号内所

Java语言Socket接口用法详解

Socket接口用法详解   在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Server,并通过accept()方法随时监听客户端的连接请求. 扩展: ServerSocket中常用的构造函数及方法 构造函数:ServerSocket(int port) 这是一个构造方法,用于在当前的服务器默认的IP地址上监听一个指定的端口,即在指定的IP和端口创建一个ServerSocket对象

C++ ofstream和ifstream详细用法以及C语言的file用法

ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就是内存空间; 在C++中,有一个stream这个类,所有的I/O都以这个"流"类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符: 1.插入器(<<) 向流输出数据.比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<'\n';就表示把字符串"W

C语言typedef的用法(转)

http://www.cnblogs.com/afarmer/archive/2011/05/05/2038201.html 一.基本概念剖析 int* (*a[5])(int, char*);       //#1 void (*b[10]) (void (*)()); //#2 double(*)() (*pa)[9];          //#3 1.C语言中函数声明和数组声明.函数声明一般是这样: int fun(int, double); 对应函数指针(pointer to funct

Java语言this关键字用法全面总结

学习Java编程的时候,无论是谁,当学到面向对象这部分内容时都会遇到一个关键字:this.很多初学者对这个关键字的都会感觉到理解不透,不明白这个神秘的"this"到底表示什么意思.按照官方正规的解释,this关键字的意义被解释为"指向当前对象的引用".这个解释非常准确并且很精炼,但它太过学术化了,导致很多初学者有点读不懂,更谈不上深入理解它的意义.本文将用大白话的形式帮助初学Java的小伙伴来深入理解this关键字的意义,并且梳理它的各种用法. 其实,我们只要把th

go语言switch语句用法

switch是最灵活的一种控制语句,在使用switch时候,需要注意: --左花括号"{"必须与switch处于同一行 --条件表达式不限制为常量或者整数 --单个case中,可以出现多个结果选项 --与C语言等规则相反,G0语言不需要break来明确退出一个case --只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case --可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个if....else的逻辑作用等同 --可以

C语言strlen()函数用法

C语言strlen()函数:返回字符串的长度 头文件:#include <string.h> strlen()函数用来计算字符串的长度,其原型为:unsigned int strlen (char *s);  s为指定的字符串 eg: #include<stdio.h> #include<string.h> int main() { char *str1 = "http://see.xidian.edu.cn/cpp/u/shipin/"; char