深入浅析C++中的#,##,和

想要灵活应用宏,离不开#和##。

"
在学习###之前,先来看一个关于"的例子:

#include <stdio.h>
#include <string.h> 

int main()
{
 const char* p1 = "Hello," "World!";  // 一个空格
 const char* p2 = "Hello," "World!"; // 多个空格
 const char* p3 = "Hello,""World!";  // 没有空格
 const char* p4 = "Hello,World!";  // 一个整串
 const char* p5 = "Nihao,""Shijie!"; // 一个不同的串 

 printf("p1 = %s, strlen(p1) = %d\n", p1, strlen(p1));
 printf("p2 = %s, strlen(p2) = %d\n", p2, strlen(p2));
 printf("p3 = %s, strlen(p3) = %d\n", p3, strlen(p3));
 printf("p4 = %s, strlen(p4) = %d\n", p4, strlen(p4));
 printf("p5 = %s, strlen(p5) = %d\n", p5, strlen(p5)); 

 return 0;
} 

输出为:

p1 = Hello,World!, strlen(p1) = 12 
p2 = Hello,World!, strlen(p2) = 12 
p3 = Hello,World!, strlen(p3) = 12 
p4 = Hello,World!, strlen(p4) = 12 
p5 = Nihao,Shijie!, strlen(p5) = 13

查看PE文件的常量字符串段,发现经过编译器优化后只存在一个Hello,World!串。

即p1,p2,p3,p4这四种写法是等价的,这一点作为之后解释#用法的前提。

字符串化操作(#)

当用作字符串化操作时,#的主要作用是将宏参数不经扩展地转换成字符串常量。

要点:

  • 宏定义参数的左右两边的空格会被忽略,参数的各个Token之间的多个空格会被转换成一个空格。
  • 宏定义参数中含有需要特殊含义字符如"或\时,它们前面会自动被加上转义字符\。

还是通过MSDN上的例子来看看容易懂:

#define F abc
#define B def
#define FB(arg) #arg
#define FB1(arg) FB(arg) 

FB(F B)
FB1(F B) 

初看到时推测这两行预编译出来后效果是一样的,但是看了使用gcc -E编译出来代码,这才理解了MSDN上对“不经扩展”有了更深刻的理解,实际的预编译后代码为:

"F B";
"abc def"; 

推测变换过程应该如下:

FB(F B) --> #F B -->"F B"
FB1(F B) --> FB1(abc def) --> FB(abc def) --> #abc def --> "abc def" 

标记连接操作(##)
将多个Token连接成一个Token。
要点:

  • 它不能是宏定义中的第一个或最后一个Token。
  • 前后的空格可有可无。

来理解一下MSDN上举的例子:

#include <stdio.h>
#define paster( n ) printf_s( "token" #n " = %d", token##n )
int token9 = 9; 

int main()
{
 paster(9);
}

paster(9);的预处理步骤应该如下:

  • paster(9);
  • printf_s( "token" #9 " = %d", token##9 );
  • printf_s( "token" "9" " = %d", token9 );
  • printf_s( "token9 = %d", token9 );

这样应该就很好理解了。

参考MSDN

Stringizing Operator(#) : http://msdn.microsoft.com/EN-US/library/7e3a913x(v=VS.110,d=hv.2).aspx
Token-Pasting Operator(##) : http://msdn.microsoft.com/EN-US/library/09dwwt6y(v=VS.110,d=hv.2).aspx

到此这篇关于C++中的#,##,和"的文章就介绍到这了,更多相关C++中的#,##,和"内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C#调用C++dll方法步骤

    C#调用C++dll的方法和步骤 其他分享涉及到的概念和方法对于像我这样比较菜的选手看起来比较费劲并且很难抓住重点,这里我总结了一段时间的研究成果供初学者救济之用,简单明了. 1.新建项目->Visual C++->Win32项目 MyDLL 注意:C++编写的dll一般是不能直接拿来C#调用,需要先新建个C++的工程把dll里的方法重新封装成可被C#外部调用的函数. 2.MyDLL.cpp里的代码如下: extern "C" _declspec(dllexport)int

  • c++中#include &lt;&gt;与#include""的区别详细解析

    首先是区别: <>先去系统目录中找头文件,如果没有在到当前目录下找.所以像标准的头文件 stdio.h.stdlib.h等用这个方法. 而""首先在当前目录下寻找,如果找不到,再到系统目录中寻找. 这个用于include自定义的头文件,让系统优先使用当前目录中定义的. 然后是使用习惯的问题: 假设A是常被包含的文件. 则A中写的应该是一些 常用的函数,和一些宏定义.而且,不能出现main函数.

  • 简单谈谈c/c++中#import、#include和@class的区别

    1. 一般来说,导入objective c的头文件时用#import,包含c/c++头文件时用#include. 2. #import 确定一个文件只能被导入一次,这使你在递归包含中不会出现问题.<标记> 所以,#import比起#include的好处就是不会引起交叉编译. #import && #class: 1. import会包含这个类的所有信息,包括实体变量和方法(.h文件中),而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,后面会再告

  • C++中const与#define的利弊分析

    C++中const与#define的区别如下: 用#define MAX 255定义的常量是没有类型的,所给出的是一个立即数,编译器只是把所定义的常量值与所定义的常量的名字联系起来,define所定义的宏变量在预处理的时候进行替换,在程序中使用到该常量的地方都要进行拷贝替换: 用const float MAX = 255; 定义的常量有类型名字,存放在内存的静态区域中,在程序运行过程中const变量只有一个拷贝,而#define 所定义的宏变量却有多个拷贝,所以宏定义在程序运行过程中所消耗的内存

  • 浅析angularJS中的ui-router和ng-grid模块

    在家里闲着无聊,正好在网上找到了一个关于angular的教程,学习了一下angular的ui-router和ng-grid这两个模块,顺便模仿着做了一个小小的东西. 代码已经上传到github上,地址在这里哟https://github.com/wwervin72/Angular. 有兴趣的小伙伴可以看看.那么然后这里我们就先来了解一下这两个模块的用法. 我们先来说说ui-router这个模块,这个模块主要是用来实现深层次的路由的.其实angular有个内置的指令ng-route,如果在项目中没

  • 浅析Python中的for 循环

    Python for 和其他语言一样,也可以用来循环遍历对象,本文章向大家介绍Python for 循环的使用方法和实例,需要的朋友可与参考一下. 一个循环是一个结构,导致第一个程序要重复一定次数.重复不断循环的条件仍是如此.当条件变为假,循环结束和程序的控制传递给后面的语句循环. for循环: 在Python for循环遍历序列的任何物品,如一个列表或一个字符串,有能力. for循环语法是: for iterating_var in sequence: statements(s) 如果一个序列

  • 浅析JavaScript中var that=this

    在阅读别人的代码时,发现别人写的代码中有这么一句:var that = this;,这代表什么意思呢?经过一番查阅,才明白是这么回事. 在JavaScript中,this代表的是当前对象. var that=this就是将当前的this对象复制一份到that变量中.这样做有什么意义呢? $('#conten').click(function(){ //this是被点击的#conten var that = this; $('.conten').each(function(){ //this是.c

  • 浅析正则表达式中的lastIndex以及预查

    依次写出下列输出内容. var reg1 = /a/; var reg2 = /a/g; console.log(reg1.test('abcabc')); // true console.log(reg1.test('abcabc')); // true console.log(reg1.test('abcabc')); // true console.log(reg1.test('abcabc')); // true console.log(reg2.test('abcabc')); //

  • 浅析JS中常用类型转换及运算符表达式

    JS中的常用类型转换(一般用强制转换): 1.强制转为整数:parseInt:写法:x = parseInt(x); 2.强制转换位小为:parseFloat:写法:x = parseFloat(x); 3.检测类型:x = parseInt(x);       alert(typeof(true)); JS中常用的运算符表达式: 1.逻辑运算符(布尔型):&& 并 :|| 或 :! 非 : 2.比较运算符:==(等于) :!=(不等于) : > :< :>=(大于等于)

  • 浅析JS中的 map, filter, some, every, forEach, for in, for of 用法总结

    1.map 有返回值,返回一个新的数组,每个元素为调用func的结果. let list = [1, 2, 3, 4, 5]; let other = list.map((d, i) => { return d * 2; }); console.log(other); // print: [2, 4, 6, 8, 10] 2.filter 有返回值,返回一个符合func条件的元素数组 let list = [1, 2, 3, 4, 5]; let other = list.filter((d,

  • 浅析GridView中显示时间日期格式的问题

    以下都是GridView基本常用的日期,时间格式 形式 语法 结果 注释 数字 {0:N2} 12.36   数字 {0:N0} 13   货币 {0:c2} $12.36   货币 {0:c4} $12.3656   货币 "¥{0:N2}" ¥12.36   科学计数法 {0:E3} 1.23E+001   百分数 {0:P} 12.25% P and p present the same. 日期 {0:D} 2006年11月25日   日期 {0:d} 2006-11-25  

  • 浅析Yii2中GridView常见操作

    本文是小编给大家收集整理些有关网络上GridView出现的大部分问题,本文做一个总结特此分享到我们平台供大家参考. 如果下面有没说到的GridView常见问题,下方留言,我会进行补充. 下拉搜索 日期格式化并实现日期可搜索 根据参数进行是否显示 链接可点击跳转 显示图片 html渲染 自定义按钮 设定宽度等样式 自定义字段 自定义行样式 增加按钮调用js操作 yii2 GridView 下拉搜索实现案例教程 yii2 GridView 日期格式化并实现日期可搜索 案例 是否显示某列案例 我们举一

  • 浅析Python 中整型对象存储的位置

    在 Python 整型对象所存储的位置是不同的, 有一些是一直存储在某个存储里面, 而其它的, 则在使用时开辟出空间. 说这句话的理由, 可以看看如下代码: a = 5 b = 5 a is b # True a = 500 b = 500 a is b # False 由上面的代码可知, 整型 5 是一直存在的, 而整型 500 不是一直存在的. 那么有哪些整数是一直存储的呢? a, b, c = 0, 0, 0 while a is b: i += 1 a, b = int(str(i)),

  • 浅析python中的分片与截断序列

    序列概念 在分片规则里list.tuple.str(字符串)都可以称为序列,都可以按规则进行切片操作 切片操作 注意切片的下标0代表顺序的第一个元素,-1代表倒序的第一个元素:且切片不包括右边界,例如[0:3]代表元素0.1.2不包括3. l=['a','b','c','d',5] 1.获取列表的前3个元素 >>> l[0:3] ['a', 'b', 'c'] >>> l[:3] ['a', 'b', 'c'] 2.获取列表的后3个元素 >>> l[-

随机推荐