C(和C++)中的宏(Macro)属于编译器预处理的范畴,属于编译器概念(而非运行期概念)。下面对常遇到的宏的使用问题做了简单总结。

关于#和##

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

#define WARN_IF(EXP) /do { if (EXP) /fprintf(stderr, "Warning:" #EXP "/n"); } /while(0)

那么在实际使用中会出现下面所示的替换过程:

WARN_IF(divider == 0);

被替换为:

do {if (divider == 0)fprintf(stderr, "Warning" "divider == 0" "/n");
} while(0);

这样每次divider(除数)为0的时候便会在标准错误流上输出一个提示信息。

而##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体的数组,并且希望在函数名和菜单项命令名之间有直观的、名字上的关系。那么下面的代码就非常实用:

struct command
{char *name;void (*function)(void);
};
#define COMMAND(NAME) {NAME, NAME ## _command }

//然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:

struct command commands[] = {COMMAND(quit);COMMAND(help);...
}

COMMAND宏在这里充当一个代码生成器的作用,这样可以在一定程度上减少代码密度,间接地也可以减少不留心所造成的错误。我们还可以n个##符号链接n+1个Token,这个特性也是#符号所不具备的。比如:

#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

typedef struct _record_type LINK_MULTIPLE(name, company, position, salary);

//这里这个语句将展开为:

//typedef struct _record_type name_company_position_salary;

##连接符号由两个#号组成,其功能是在带参数的宏定义中将两个子串(token)连接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最下语法单元。

#符是把传递过来的参数当成字符串进行替代。

下面看看它们是怎么工作的。这是MSDN上的一个例子。假设程序中已经定义了这样一个带参数的宏:

#define paster(n) printf("token" #n "=%d", token##n)

同时又定义了一个整形变量:int token9 = 9;

现在在主程序中以下面的方式调用这个宏: paster(9);

那么在编译时,上面的这句话被扩展为:printf("token" "9" "=%d", token9);

注意到这个例子中,paster(9);中的这个"9"被原封不动的当成了一个字符串,与"token"连接在了一起,从而成为了token9。而#n也被"9"所替代。可想而知,上面程序运行的结果就是在屏幕上打印出token9=9

C语言宏与单井号(#)和双井号(##)相关推荐

  1. C/C++中单井号与双井号的使用

    C/C++中单井号与双井号的使用 作者:delnabla 日期:2009-12-24 字体大小: 小 中 大 在宏定义中经常会用到#和## 单井号(#) 把传递过来的参数当成字符串进行替代 如下示例: ...

  2. c语言井号字符什么意思,关于c语言宏定义中的单#(井号)和双#(井号)

    看了这篇文章后了解了,但是文章中的例子比较特别,我在这里加个注释好了. 单井号就是将后面的 宏参数 进行字符串操作,就是将后面的参数用双引号引起来 双井号就是用于连接. 比如文章中的例子: #defi ...

  3. 单井号(#)和双井号(##)

    在宏定义中经常会用到#和## 单井号(#) 把传递过来的参数当成字符串进行替代 如下示例: 程序代码 #define display(name) printf(""#name&qu ...

  4. C语言中 井号#、双井号##

    在 C 语言中,井号 "#" 是一个预处理器指令的符号,用于在编译之前处理代码.预处理器指令以 "#" 开始,以换行符结束. 常见的预处理器指令包括: #inc ...

  5. C语言宏定义中#define中的井号#的使用

    #include <stdlib.h> #include <stdio.h> #include <string.h>typedef enum {E_1,E_2,E_ ...

  6. 【原创】【C语言】‘#’(井号)和‘##’(双井号)用法

    一,这是个什么玩意 有时候在宏定义或打印语句中经常见到#井号或者##双井号,看着很牛逼的用法 二,有毛用啊 #:表示字符串化:##:表示concatenate(连接) 具体就是,#号的功能是将其后面的 ...

  7. ##(两个井号)和#(一个井号)都是什么意思C and C++ 2008-09-23 21:18:27 阅读1106 评论0 字号:大中小 订阅 .

    ##(两个井号)和#(一个井号)都是什么意思 C and C++ 2008-09-23 21:18:27 阅读1106 评论0   字号:大中小 订阅 连接符 ##(两个井号) 不知道什么符 #(一个 ...

  8. 井号#和2井号##和艾特井号@#的使用

    // vs2017使用 //井号#和2井号##和艾特井号@#的使用.cpp : Defines the entry point for the console application. //#incl ...

  9. C语言(C++语言)中##(两个井号)和#(一个井号)用法[转]

    C语言(C++语言)中的宏(Macro)属于编译器预处理的范畴,属于编译期概念(而非运行期概念).下面对常遇到的宏的使用问题做了简单总结. 关 于#和## 在C语言的宏中,#的功能是将其后面的宏参数进 ...

最新文章

  1. angular绑定数据_Angular中的数据绑定说明
  2. 单引号、双引号 转义符
  3. discuz回贴通知插件实现-配置邮件服务器
  4. WWDC 2014 Session笔记 - 可视化开发,IB 的新时代
  5. 【Pytorch神经网络理论篇】 30 图片分类模型:Inception模型
  6. Vim快速移动光标至行首和行尾
  7. android toast_Android Toast
  8. Duilib--->Duilib库介绍总结概述
  9. Sicily 1094 Cude解题报告
  10. 微信小程序实战教程,适合初学者快速入门。
  11. VS2016 调用matlab脚本 ——缺少mclmcr.dll
  12. 舌尖上的职场(三)我来买单!
  13. CIO圈子里的“老行家”:太平绅士赖锡璋
  14. 安信可LoRaWAN网关 RG-02 接入TTN平台,控制多节点LoRaWAN模组。
  15. ionic3 生命周期方法
  16. 栈的应用 - 波兰式与逆波兰式
  17. [4G5G专题-93]:流程 - 4G LTE 终端在RRC 连接状态下的小区切换通用过程
  18. 基于机器学习的时序数据预测方法
  19. git中提示 please tell me who you are
  20. OpenGL PBO渲染视频数据 [转]

热门文章

  1. Mybatis基本配置和搭建
  2. 蓝桥杯 ADV-202 算法提高 最长公共子序列
  3. [leetcode]101.对称二叉树
  4. 覆盖索引最左前缀原则索引下推
  5. 程序员圣诞节相册源码_程序员分享圣诞刷屏源码,这次朋友圈千万不要再@微信官方了!...
  6. 关闭Windows自动更新
  7. 传递参数命令——xargs
  8. Mysql价格降低20%应该怎么写_mysql优化20条原则
  9. python网络编程爬虫_Python爬虫--网络编程
  10. 割平面法只能求解纯整数规划吗_离散规划 解法