C/C++ 中的宏/Macro
|
宏(Macro)本质上就是代码片段,通过别名来使用。在编译前的预处理中,宏会被替换为真实所指代的代码片段,即下图中 Preprocessor 处理的部分。
C/C++ 代码编译过程 - 图片来自 ntu.edu.sg 根据用法的不同,分两种,Object-like 和 Function-like。前者用于 Object 对象,后者用于函数方法。 C/C++ 代码编译过程中,可通过相应参数来获取到各编译步骤中的产出,比如想看被预处理编译之后的宏,使用 $ gcc -E macro.c 宏的定义通过 #define NAME_OF_MACRO value 比如,以下代码定义了一个名为 #define BUFFER_SIZE 1024 使用时, foo = (char *) malloc (BUFFER_SIZE); 使用预处理器编译: $ gcc -E test.c 编译结果: foo = (char *) malloc (1024); 多行宏的定义是跟随 #include <stdio.h> 多行的宏经过编译后会还原到一行中。 test.c #include <stdio.h> 编译后: int main() {
宏展开时的顺序宏的展开是在处理源码时按照其出现位置进行的,如果宏定义有嵌套关系,也是层层进行展开,比如: #include <stdio.h> 首先遇到 int main() {
其展开的顺序并不是宏定义时的顺序,为了验证,可将上面示例代码中两个宏的定义调换一下,得到: -#define GREETING_NAME "wayou" 再次编译查看产出,会发现没有区别,也不会报 像下面这样,当宏存在覆盖时,会以新的为准,其结果为 37。 #define BUFSIZE 1020 Object-like 宏Object-like 类型的宏看起来就像普通的数据对象,故名。多用于数字常量的情形下。且宏名一般使用全大写形式方便识别。像上面示例中,都是 Object-like 的。 Function-like 宏也可定义出使用时像是方法调用一样的宏,这便是 Function-like 类型的宏。 #define lang_init() c_init() 函数类型的宏只在以方法调用形式使用时才会被展开,即名称后加括号,否则会被忽略。当宏名和函数名重名时,这一策略就会显得有用了,比如: extern void foo(void); 这里 函数类型的宏在定义时需注意,宏名与后面括号不能有空格,否则就是普通的 Object-like 类型对象。 #define lang_init () c_init() 宏的参数函数类型的宏,可以像正常函数一样指定入参,入参需为逗号分隔合法的 C 字面量。 #define min(X, Y) ((X) < (Y) ? (X) : (Y)) 入参中的括号入参中只需要括号对称,但不要求方括号或花括号成对出现,所以下面的代码: macro (array[x = y, x + 1]) 其入参实际为 入参的展开入参本质上也是宏,对象类型的宏,在函数宏展示时,这些参数也被展示到了函数宏的函数体里。 min (min (a, b), c) 首先被展开成: min (((a) < (b) ? (a) : (b)), (c)) 然后进一步展开成(此处换行为方便阅读,实际编译后没有): ((((a) < (b) ? (a) : (b))) < (c) 参数的缺省函数宏在使用时其入参可缺省,但不能全部缺省,至少提供一个入参。 min(, b) → (( ) < (b) ? ( ) : (b)) 字符化/Stringizing如果函数宏中入参在字符串中,是不会被展开的,它就是普通的字符串字面量,这样的结果是符合预期的。 #define foo(x) x, "x" 但如果确实想将入参展开成字符串,可在使用入参时,加上 #define WARN_IF(EXP) \ 此处 拼接通过 宏拼接一般用在需要拼接的宏是来自宏参数的情况,其他情况,大可直接将两个宏写在一起即可,用不着 考察下面这个场景,其中命令名重复出现: struct command 通过定义宏配合拼接,可达到精简代码的目的: #define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
不定参数像普通函数一样,函数类型的宏也可定义接收不定参数。 #define eprintf(…) fprintf (stderr, __VA_ARGS__) 调用时,命名参数后面,包括逗号都会进入到 eprintf ("%s:%d: ", input_file, lineno)
// 编译后:
C++ 中可这么写: #define eprintf(args…) fprintf (stderr, args) 不定参数与命名参数混合的情况不定参数为命名参数后面省略的部分。 #define eprintf(format, …) fprintf (stderr, format, __VA_ARGS__) 预设的宏标准库及编译器中预设了一些有用的宏,可以在这里 查阅。 取消和重置宏当某个宏不再使用时,可通过 #define FOO 4 两个宏相似的定义满足以下条件时,我们认为两者是相似的:
比如,下面这些是相似的: #define FOUR (2 + 2) 而下面这些则不然: #define FOUR (2 + 2) 宏重复定义时的表现对于使用了 而如果说一个已经存在的宏,并没有注销,重复定义时,如果相似,则新的定义会忽略,如果不相似,编译器会报警告同时使用新定义的宏。这允许在多个文件中定义同一个宏。 相关资源 |
C/C++ 中的宏/Macro的更多相关文章
- uboot中的中断macro宏
目录 uboot中的中断macro宏 引入 内存分配 流程概览 普通中断 保存现场 中断函数打印具体寄存器 恢复现场 软中断 空间获取 保存现场 附录速记 疑惑待解 title: uboot中的中断m ...
- C中的宏
1. 简单宏定义 简单的宏定义有如下格式: [#define指令(简单的宏)] #define 标识符替换列表 替换列表是一系列的C语言记号,包括标识符.关键字.数.字符常量.字符串字面量.运算符和 ...
- flask第30篇——宏macro和import标签
宏是Jinja2特有的,像Django则没有这个. 先新建一个项目macroDemo: 然后在templates文件夹中新建index.html文件,并在代码中返回渲染后的文件: 然后回到index. ...
- flask中的宏
对于flask中的宏编程.我们使用 macro 来对宏起个名称 宏编程 对于我们来说是减少了代码的重用.以及简化了标签的操作,对与开发效率有很大的提升, 在html中.相信大多数都用到了.input ...
- zabbix上的宏(macro)介绍
宏:macro,预设的文本替换模式: 宏是一种抽象概念(Abstraction),它根据一些列预定义的规则替换一定的文本模式,而解释或编译器在遇到宏时会自动进行这一模式替换.类似地,zabbix基于宏 ...
- Makefile中用宏定义进行条件编译(gcc -D)/在Makefile中进行宏定义-D【转】
本文转载自:http://blog.csdn.net/maopig/article/details/7230311 在源代码里面如果这样是定义的:#ifdef MACRONAME//可选代码#en ...
- Flask基础(15)-->模板代码的复用【宏(Macro)、继承(Block)、包含(include)】
宏 对宏(macro)的理解: 把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串 为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用 需要在多处重 ...
- js中的宏任务与微任务
如果你已经知道了js中存在宏任务和微任务,那么你一定已经了解过promise了.因为在js中promise是微任务的一个入口. 先来看一道题: setTimeout(function(){ conso ...
- C语言的宏macro的使用
C's Macro Introduction 1.The Connect Macros: ## 这是一个预处理连接符,这个操作符主要用来将两个符号连接成为一个完整的宏符号.通过下面的代码,可以看到其具 ...
随机推荐
- CCF-CSP题解 201903-3 损坏的RAID5
先吐槽先吐槽!因为输入太大,需要用fgets,读n个字符或读到回车终止. char *fgets(char *str, int n, FILE *stream) 因为scanf模拟考试T了10+次.因 ...
- java基础集合简介Set(三)中
今天主要说夏set集合,每天抽出一个小时总结下,生活会更加美好! --< java.util >-- Set接口: 数据结构:数据的存储方式: Set接口中的方法和Collection中方 ...
- SpringBoot电商项目实战 — Zookeeper的分布式锁实现
上一篇演示了基于Redis的Redisson分布式锁实现,那今天我要再来说说基于Zookeeper的分布式现实. Zookeeper分布式锁实现 要用Zookeeper实现分布式锁,我就不得不说说zo ...
- WPF的DataGrid用法-小白向
前几天打算尝试下DataGrid的用法,起初以为应该很简单,可后来被各种使用方法和功能实现所折磨.网络上的解决方法太多,但也太杂.没法子,我只好硬着头皮阅览各种文献资料,然后不断的去尝试,总算小有成果 ...
- Python面向对象-概念、类、实例
OOP——Object Oriented Programming,面向对象编程,是一种程序设计思想.该思想将对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 区别于面向过程的程序设计即把计 ...
- C# MySql Transaction Async
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- windows + flutter +android+ vscode 安装配置运行流程(详细版本)
flutter 是由谷歌发布的一个全新的响应式.跨平台.高性能的移动开发框架,可以快速在iOS和Android上构建高质量的原生用户界面. 框架特点 快速开发:Flutter的热重载可以快速地进行测试 ...
- 云原生技术之Docker入门
1. 为什么需要容器? 下图是一个比较传统的软件架构 做过java的同学可能对上图的架构方式比较了解,我们通常会将一个应用程序生成一个war包,放到一个tomcat容器当中并在一台虚拟机(VM)中启动 ...
- CentOS 7上的系统管理之:Systemd和systemctl
参考资料: Chapter 10. Managing Services with systemd Red Hat Enterprise Linux 7 | Red Hat Customer Porta ...
- 我如何通过K8S开发认证(CKAD)考试
题记:笔者最近经过3个多月的空余时间准备,终于通过了K8S开发认证(CKAD)的考试,在这里简单给大家分享一下经验. 一,先科普下CKAD 众所周知,Kubernetes在容器编排器大战中脱颖而出后, ...
