C/C++中宏总结C程序的源代码中可包括各种编译指令,这些指令称为预处理命令。虽然它们实际上不是C语言的一部分,但却扩展了C程序设计的环境。

  ANSI标准定义的C语言预处理程序包括下列命令:   

  #define,#error,#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。所有的预处理命令都已符号#开头。

1、#define  

  命令#define定义了一个标识符及一个串。在源程序中每次遇到该标识符时,均以定义的串代换它。ANSI标准将标识符定义为宏名,将替换过程称为宏替换。命令的一般形式为:

#define identifier string 

  • 该语句没有分号。标识符和字符串之间可以有任意个空格,但是一旦字符串开始,只能由新的一行结束。
  • 宏名称定义后,即可成为其他宏名定义的一部分。
  • 宏替换仅仅是以字符串代替标识符,前提是宏标识符必须独立的识别出来,否则不进行替换。比如#define 123 a number就不能独立识别。
  • 若字符串长于一行,可以在行末尾使用‘\’续行。

2、#error

  处理器命令#error强迫编译程序停止编译,主要用于程序调试。

3、#include

  命令#include使编译程序将另一源文件嵌入带有#include的源文件,被读入的源文件必须用双引号或尖括号括起来。

  • 如果显式路径名为文件标识符的一部分,则仅在子目录中搜索被嵌入文件。即包含路径只搜索路径内。
  • 如果文件名用双引号括起来,则首先检索当前工作目录。如果未发现文件,则在命令行中说明的所有目录中搜索。如果仍未发现文件,则搜索实现时定义的标准目录。
  • 如果没有显式路径名且文件名被尖括号括起来,则首先在编译命令行中的目录内检索。

4、条件编译指令

  有几个命令可对程序源代码的各部分有选择地进行编译,该过程称为条件编译。商业软件公司广泛应用条件编译来提供和维护某一程序的许多顾客版本。

  #if、#else,#elif及#endif   #if的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif标识一个#if块的 结束 

  #if constant-expression

  statement sequence

  #endif 

  跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof(sizeof也是编译时求值)。

  if、else、elif(elseif)、endif与代码中的条件判断类似。

  # ifdef 和# ifndef 

  条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示"如果有定义"及"如果无定义"。 
# ifdef的一般形式是: 

  # ifdef macroname

  statement sequence

  #endif

#ifdef与#ifndef可以用于#if、#else,#elif语句中,但必须与一个#endif。

5、undef

  命令#undef 取消其后那个前面已定义过的宏名定义。一般形式为:

  #undef macroname

6、#line

  命令# line改变__LINE__与__FILE__的内容,它们是在编译程序中预先定义的标识符。命令的基本形式如下:

  # line number["filename"] 

  其中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源程序中当前行号,文件名为源文件的名字。命令# line主要用于调试及其它特殊应用。

7、预定义的宏名

  ANSI标准说明了C中的五个预定义的宏名。它们是:

  __LINE__ 

  __FILE__ 

  __DATE__ 

  __TIME__ 

  __STDC__ 

  如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。

  __LINE__及__FILE__宏指令在有关# line的部分中已讨论,这里讨论其余的宏名。

  __DATE__宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。

  源代码翻译到目标代码的时间作为串包含在__TIME__中。串形式为时:分:秒。

  如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。编译C++程序时,编译器自动定义了一个预处理名 
字__cplusplus,而编译标准C时,自动定义名字__STDC__。

  注意:宏名的书写由标识符与两边各二条下划线构成。

8、C、C++宏体中出现的#,#@,##

宏体中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个 
双引号。

而##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变 
量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那就可以使用:宏参数## 
固定部分。当然还可以n个##符号连接 n+1个Token,这个特性也是#符号所不具备的。

#@的功能是将其后面的宏参数进行字符化。

9、C宏中的变参...

...在C宏中称为Variadic Macro,也就是变参宏。比如:

#define myprintf(templt,...) fprintf(stderr,templt,__VA_ARGS__)

或者#define myprintf(templt,args...) fprintf(stderr,templt,args)

  第一个宏中由于没有对变参起名,我们用默认的宏__VA_ARGS__来替代它。第二个宏中,我们显式地命名变参为args,那么我们在宏定义中就可以 
用args来代指变参了。同C语言的stdcall一样,变参必须作为参数表的最后有一项出现。当上面的宏中我们只能提供第一个参数templt时,C 
标准要求我们必须写成: myprintf(templt,);的形式。这时的替换过程为:myprintf("Error!\n",);替换为: 
fprintf(stderr,"Error!\n",).

这是一个语法错误,不能正常编译。这个问题一般有两个解决方法。首先,GNU CPP提供的解决方法允许上面的宏调用写成: 
myprintf(templt);而它将会被通过替换变成: fprintf(stderr,"Error!\n",);

很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式:

#define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__)

这时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下: 
myprintf(templt);被转化为: fprintf(stderr,templt);

这样如果templt合法,将不会产生编译错误。

c++宏使用总结【转】的更多相关文章

  1. Visual Studio 宏的高级用法

    因为自 Visual Studio 2012 开始,微软已经取消了对宏的支持,所以本篇文章所述内容只适用于 Visual Studio 2010 或更早期版本的 VS. 在上一篇中,我已经介绍了如何编 ...

  2. VC 中与字符串相关的宏 _T、TEXT,_TEXT、L 的作用

    CSDN原博文:http://blog.csdn.net/houkai363/article/details/8134787 遇到了:不能将参数 1 从“const char [5]”转换为“LPCT ...

  3. 【转】linux内核中writesb(), writesw(), writesl() 宏函数

    writesb(), writesw(), writesl() 宏函数 功能 : writesb()    I/O 上写入 8 位数据流数据 (1字节) writesw()   I/O  上写入 16 ...

  4. c++宏定义命令

    在程序开始以#开头的命令,他们是预编译命令.有三类预编译命令:宏定义命令.文件包含命令.条件编译命令:今天聊聊宏定义: 宏定义命令将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串来代替 ...

  5. dll导入导出宏定义,出现“不允许 dllimport 函数 的定义”的问题分析

    建立dll项目后,在头文件中,定义API宏 #ifndef API_S_H #define API_S_H ...... #ifndef DLL_S_20160424 #define API _dec ...

  6. VC++/MFC 最常用宏和指令

    1.#include指令  包含指定的文件,最基本的最熟悉的指令,编程中不得不用,包含库文件用双尖括号,包含自定义头文件用双引号. 2.#define指令   预定义,通常用它来定义常量(包括无参量与 ...

  7. [Sass]混合宏的参数

    [Sass]混合宏的参数--传一个不带值的参数 Sass 的混合宏有一个强大的功能,可以传参,那么在 Sass 中传参主要有以下几种情形: A) 传一个不带值的参数 在混合宏中,可以传一个不带任何值的 ...

  8. [Sass]混合宏

    [Sass]混合宏-声明混合宏 如果整个网站中有几处小样式类似,比如颜色,字体等,在 Sass 可以使用变量来统一处理,那么这种选择还是不错的.但当你的样式变得越来越复杂,需要重复使用大段的样式时,使 ...

  9. BOOST_AUTO宏

    在boost中,有个非常不错的宏BOOST_AUTO(),它的作用是自动给var定义类型,适合function()函数返回的值的类型. int function() { ; } main() { BO ...

  10. C++预定义宏

    C/C++宏体中出现的#,#@,##: - #的功能是将其后面的宏参数进行字符串化操作(stringfication),就是对它所引用的宏变量通过替换后在其左右各加上一个双引号 -##被称为连接符(c ...

随机推荐

  1. C++多线程环境下的构造函数

    多线程的环境里,我们总不可避免要使用锁.于是一个常见的场景就是: class ObjectWithLock { private: std::mutex mtx_; SomeResType shared ...

  2. Windows进程崩溃问题定位方法

    Linux上进程崩溃通常会生成core文件,用gdb打开后执行bt命令即可查看堆栈.而在Windows平台上,我们通常会采用MiniDumpWriteDump来进行堆栈转储,而这需要对系统Api有一定 ...

  3. Beagleboard xm sd卡恢复

    来源于:http://circuitco.com/support/index.php?title=BeagleBoard-xM windows下恢复更加简单,推荐windows系统下恢复. To in ...

  4. Linq Group By

    TableA { Id int, Name string, Group  int Score int } 从 Id Name Group Score 1 张三 A 70 2 李四 A 80 3 王五 ...

  5. position窗口居中

    position的四个属性值: relative absolute fixed static 下面分别讲述这四个属性. <div id="parent">     &l ...

  6. MVC 本地运行可以发布到IIS 报Sorry, an error occurred while processing your request.解决方案

    发布MVC程序的时候经常遇到这种情况,每次都要搞好久才找到问题.最终找到解决办法: 报500错误大部分的情况还是程序存在异常,但全部被MVC错误拦截成了友好提示, 只要在Web.config下添加一行 ...

  7. Page事件执行顺序

    Page 执行中将按照如下顺序激活事件: Page.PreInit Page.Init Page.InitComplite Page.PreLoad Page.Load Page.LoadComple ...

  8. hadoop2的高可用性

    1   hadoop2  namenode由一个节点变成两个节点,同时在线,且同时只有一个是活跃的,如果一个出了问题,另外一个立即接替:没必要配置Secondary NameNode.Checkpoi ...

  9. 将NSString转换成UTF8编码的NSString

    在使用网络地址时,一般要先将url进行encode成UTF8格式的编码,否则在使用时可能报告网址不存在的错误,这时就需要进行转换 下面就是转换函数: NSString *urlString= [NSS ...

  10. [Java Basics3] XML, Unit testing

    What's the difference between DOM and SAX? DOM creates tree-like representation of the XML document ...