C++ macro(宏)使用小结
谈起C++中的宏,我们第一个想到的应该就是“#define”,它的基本语法长得像这样:
#define macroname(para1, para2, para3, ... ,paran) macro-body
宏的声明和普通的函数声明很像,但是两者之间有本质的区别:C++函数在运行时(runtime)才执行代码段;而宏则是在预编译时期(preprocessor)执行代码段。下面简单介绍一下几个宏的应用。
一、考虑下面的代码段:
#define PLUS_ONE(x) ((x) + 1)
int x=PLUS_ONE();
>>>x=((12) + 1)
>>>x=13
这是最简单的应用,用macro-body替换macroname;和C函数不一样的是,宏是没有返回值的,本身表达式的值将作为返回值传回。
二、考虑下面的代码段:
#define x 1+2
int t=x*;
std::cout<<t<<'\n';
那么输出t的值会是什么呢?答案也许不是你期望中的9,而是7。原因很简单,#define的替换实质上是表达式的替换,将上述代码里的宏展开后将变成这样:
int t = 1 + 2 * 3; //t = 7
这种结果显然不是我们愿意看到的,而且这种错误是很难察觉的,完全没有语法错误和语义错误;不过要解决这个问题也很容易,将表达式括起来就行了:
#define x (1+2)
>>>int t = (1+2)*3=9
不过这种使用宏的方法是不推荐的,因为容易引起一些不必要且难以察觉的错误。在C++里,我们完全可以这样声明x,也能达到同样的目的:
const int x=; //声明常数推荐用const关键字
三、Preprocessor预先定义好的值;
__FILE__ :编译的文件的绝对路径;
__LINE__ :当前行号;
__TIME__ :当前时间;
__DATE__ :当前日期。
四、C语言中macro的设计初衷之一是关于内联函数。如果我们定义一个求较大值的MAX函数:
#define MAX(a,b) ((a)>(b)?(a):(b))
>>>int myInt=MAX(x,y)
>>>int myInt=((x)>(y)?(x):(y)) //上一行调用的宏将直接这样插入到代码段里
如果我们将MAX写成一个一般的函数,那么我们调用MAX将有以下两个过程:①调用名称为MAX的函数 ②将比较得到的值返回。显然在这两种方案中,macro的方案更较高效,因为它省略的函数调用的开销,直接能得到结果(相当于inline函数的用法,这里不展开讲inline了)。
五、字符串操作函数
看如下macro定义:
#define MAX(a,b) ((a)>(b)?(a):(b))
这里的参数a和b实际上都是以string的方式传递的,例如MAX(10,12)返回的是字符串"12",而不是整型的12。预处理器提供两种方式处理string类型的参数传递。
1、stringizing operator '#':返回一个C风格的字符串;
#define TEST(n) std::cout << #n << " is " << (n) << std::endl
>>>int x= 6;
>>>TEST(x*2); //std::cout << "x*2" << " is " << (x*2) << std::endl
最终得到的结果是:x*2 is 12
那么这种看似tricky的语法有什么用呢?其实,当上述C++代码被翻译成机器码时,所有和变量x相关的概念或者语句都会被“消灭”,因为变量只会存在于C++代码层;而通过stringizing operator,让我们以字符串的形式保存一部分C++源代码变得可能,这可以应用于写一些“自我诊断(纠错)”的函数中,有机会再深入介绍吧!
2、string concatenation operator '##':顾名思义,字符串连接算子:
#define TEST2(type) type ones_##type
>>>TEST2(int);
>>>int ones_type; //这个宏的功能其实就是声明一个任意类型的,如int的变量,名字叫做ones_type
这个没感觉有什么特别的用途,trick。
C++ macro(宏)使用小结的更多相关文章
- uboot中的中断macro宏
目录 uboot中的中断macro宏 引入 内存分配 流程概览 普通中断 保存现场 中断函数打印具体寄存器 恢复现场 软中断 空间获取 保存现场 附录速记 疑惑待解 title: uboot中的中断m ...
- 转 freemarker macro(宏)的使用
有人说用freemarker,但没有用到它的宏(macro),就=没有真正用过freemarker.说的就是宏是freemarker的一大特色. 宏的定义可以查看相关的文档,里面介绍得很清楚,下面来看 ...
- [Umbraco] macro(宏)在umbraco中的作用
macro在umbraco中是一个核心的应用,它是模板页中用于动态加载内容的标签(模板指令),宏可以是基于XSLT文件创建,亦可以是基于ASP.NET用户控件创建 在develop下的Macros中创 ...
- Rust 1.7.0 macro宏的复用 #[macro_use]的使用方法
Rust 1.7.0 中的宏使用范围包含三种情况: 第一种情况是宏定义在当前文件里.这个文件可能是 crate 默认的 module,也可能是随意的 module 模块. 另外一种情况是宏定义在当前 ...
- FreeMarker学习(宏<#macro>的使用)
原文链接:https://my.oschina.net/weiweiblog/blog/506301?p=1 用户定义指令-使用@符合来调用 有两种不同的类型:Macro(宏)和transform( ...
- CMake语法—普通变量与包含、宏(Normal Variable And Include、Macro)
目录 CMake语法-普通变量与包含.宏(Normal Variable And Include.Macro) 1 CMake普通变量与包含.宏示例 1.1 代码目录结构 1.2 根目录CMakeLi ...
- 用VBA宏从一个工作薄复制内容到另一个工作薄
我们项目管理有两个工作薄,一个里面有多个表,每天建一个,记录当天项目,另一个工作薄,有多个表,其中一个是所有项目汇总. 以前都是第一个工作薄一个表做完,再复制粘贴到第二个工作薄的汇总表中. 写了个VB ...
- Excel 将A表的基础数据拼接到B表中来-三种方法: ctrl+回车, VLOOKUP()函数,宏
A表 基础信息表 B表 业务信息表 将a表中的基础数据 拼接到B表的后面, 应用场景是: B表很多数据,很繁乱,名字不一定全, A表也是比较多的行,B表乱:比如有8行有李晨的,却只有3行是范仲淹的, ...
- Confluence 6 用户宏示例 - Formatted Panel
下面的用演示了如果还写一个用户宏,并在这个宏中创建一个格式化的面板,并且指定颜色.将会创建下面的面板: (Title) 注意:这个面板的标题为空,如果你没有给这个面板标题参数的话. Macro n ...
- Confluence 6 空间中的常用宏
小组空间(Team Spaces): 介绍小组:User Profile Macro 将会对 Confluence 的用户显示属性的简单摘要,属性照片,联系方式. 在你小组中分享通知和新闻:The B ...
随机推荐
- 通过代码的方式完成WCF服务的寄宿工作
使用纯代码的方式进行服务寄宿 服务寄宿的目的是为了开启一个进程,为WCF服务提供一个运行的环境.通过为服务添加一个或者多个终结点,使之暴露给潜在的服务消费,服务消费者通过匹配的终结点对该服务进行调用, ...
- Linux 安装 PHP 环境
使用虚拟机玩linux时,发现CentOS中的php版本是5.1.6.如果要安装新版的php,需要把旧的版本删除. 先查看下php版本:# php -v 如果执行该命令提示该命令不存在,那么可以通过以 ...
- Python数学运算的一个小算法(求一元二次方程的实根)
请定义一个函数quadratic(a, b, c),接收3个参数,返回一元二次方程:ax² + bx + c = 0的两个解. #!/usr/bin/env python # -*- coding: ...
- spring mvc绑定复杂对象报错“Could not instantiate property type [com.ld.net.spider.pojo.WorkNode] to auto-grow nested property path: java.lang.InstantiationException: com.ld.net.spider.pojo.WorkNode”
解决方法之一: 1.确保所有的Pojo都包含了默认的构造器:
- Ettercap中间人攻击--介绍
前言 Ettercap有四种界面:Text,Curses,GTK2,Daemonize. -T 命令行界面,只显示字符.通常与配套的参数有-q(安静模式),加上该选项,则不会显示抓到的数据包 ...
- Atitit.为什么小公司也要做高大上开源项目
Atitit.为什么小公司也要做高大上开源项目 1. 为什么手头有很多加急的事情还要做高大上开源项目??1 2. 从长远看,发展 高大上开源项目计划对于解决我们在应急项目正面临着的种种严峻问题也大有裨 ...
- Docker: 解决Docker无法在电信网络中访问外网
在电信网络中,Docker在build和run时会无法访问外网,原因是docker的默认dns地址是8.8.8.8,由于众所周知的原因,我们需要改写这个地址,方法如下: 修改/etc/sysconfi ...
- 安卓开发-问题集-Description Resource Path Location TypeUnparsed aapt error(s)! Check the console for output.
今天在安卓项目中 res-drawable-hdpi 替换图片的时候出现这个问题 问题现象为项目显示一个叉号,但是在项目内容的任何文件都不显示叉号, 搞了半天没发现是什么问题,然后就去项目的文件夹下 ...
- 安卓第四天笔记-Sqlite
安卓第四天笔记-Sqlite 1.数据库的创建运行与更新 1.1.创建一个类继承SqliteOpenHelper 1.2.创建构造方法 /** * 数据库创建类 * @author 刘楠 * * 20 ...
- IOS中的多核并发编程GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispat ...