文章结构如下:

宏的定义
宏的替换与代码展开
宏的替换产生的问题
获取宏参数名称
宏参数的结合
宏的取消
宏定义的换行连接
标准预处理宏
宏(macro)是基于#define所实现的另一种预处理功能。
与基本的#define定义的是常量相比,宏(macro)允许多个参数化替换,参数中可以是固定的字符串,也被一些变量所替代。这个替换的操作将在预编译的时候完成。
宏(macro)的作用是用比较简单的方式表示复杂的函数调用,以提高程序的可读性。

宏的定义
宏的定义遵循以下格式:

#define macro_name(list_of_var) substitution_string
macro_name()括号中支持0个到多个参数,参数间用,分隔开;定义的最后一部分是宏的表达式,在实际的预处理中会将该表达式替换到代码正文中去,这里要特别注意的是替换这个词(坑)。
下面是一个宏的定义示例,用于求出两个数的乘积:

#define multi(x, y) x * y;
接收参数的宏看起来就像是函数。

宏的替换与代码展开
编译器在预编译时会把用到宏的代码替换成宏定义的表达式。
如上面定义的宏multi,可以如下代码中使用:
代码文件: macro.c

int multi = multi(2, 3);
在预编译时,替换操作的查看可以通过编译器命令查看效果,命令如下:

gcc -E macro.c > result.txt
// 或者 使用 -P 参数可以过滤到一些编译器链接标记符号信息
gcc -E -P macro.c > result.txt
打开result.txt则会看到宏的替换后代码展开的效果为:

int multi = 2 * 3;; // 确实是两个";" 纯替换效果
最后根据表达式的运算,multi的值为6。

宏的 替换 产生的问题
如上所示,宏 替换 操作是纯表达式的原文替换,连末尾的“;”都不会落下。那么,这在某此情况下会产生一些不符合预期的结果。
继续以宏multi为例,如果改一下参数如下:

int multi = multi(2, 3 + 2);
以上的代码我们希望得到的结果是10,但代码输出却是8。我们使用预编译命令将预处理过宏的代码展开查看如下:

int multi = 2 * 3 + 2;;
可见,由于预处理时,对宏的操作只是替换,经替换后的代码由于运算符的优先级问题,结果可能与预期的是不一致的。在这个例子中,会先运行乘法得到6后再加上2结果为8。以上的这种问题还会发生在”++”、”- -“运算上。
所以在有参数的宏中,为了使用运算与预期的一致,需要对参数增加左右括号以保证运算结果,并且取消在宏定义处”;”的结尾。定义写法如下:

#define multi(x, y) (x)*(y)
获取宏参数名称
在宏的表达式中,配合”#”的使用可以获取宏参数的名称。
如下定义了一个打印字符串的例子:

#define PrintStr(var) printf(“var=%s”, var)
在上面的表达式中,只能固定的打印出参数名称var=,但在项目中参数var的名称和其值一样是不固定的,有时候需要能够知道该var的命名,则可以这么做:

#define PrintStr(var) printf(#var"=%s" , var)
则可以获取当前的字符串名称的命名。运用如下:

#define PrintStr(var) printf(#var"=%s" , var)
struct Student{
char name[20];
int age;
};
int main (int argc, char ** argv) {
struct Student mine = {“jack.ma”, 20};
PrintStr(mine.name);
printf("\n\n");
PrintStr(“break.line\n”);
return 0;
}
则打印输出如下:
PrintStr
PrintStr

再使用gcc命令把宏展开后的代码贴出来看一下:

struct Student{
char name[20];
int age;
};
int main (int argc, char ** argv) {
struct Student mine = {“jack.ma”, 20};
printf(“mine.name”"=%s" , mine.name);
printf("\n\n");
printf("“break.line\n”""=%s" , “break.line\n”);
return 0;
}
可以看出,在宏的表达式中#var的结果代码在预编译的时候会把参数的原文一字不动的替换到代码中。这样来获取参数的命名。

宏参数的结合
宏的表达式中还支持对宏参数进行拼接,定义如下:

#define macro_name(var1, var2, var3) var1##var2##var3
在上面的例子中,对宏的三个参数进行了拼接,拼接规则是在宏的表达式中,参数与参数之间用##连接起来,中间不能有空格。这个功能可以用于合成变量名称,或是从两个或多个宏参数中生成一个格式控制字符串。
用法示例:

#define PrintStr(var) printf(#var"=%s", var)
#define join(vA, vB, vC) vA##vB##vC
int main (int argc, char ** argv) {
char * name_car_1 = “Fit”;

PrintStr(join(name, _car_, 1));
printf("\n\n");
return 0;

}
使用gcc命令对宏进行代码展开后的预处理代码为:

int main (int argc, char ** argv) {
char * name_car_1 = “Fit”;
printf(“join(name, car, 1)”"=%s", name_car_1);
printf("\n\n");
return 0;
}
所以如上代码运行结果为:

join(name, car, 1)=Fit
宏的参数结合功能可以通过替换功能组合成一个新的参数或变量名称,但值得注意的是这些操作都是在编译的预处理过程中实现的,即代码运行之前就已经决定了新的参数或变量的最终形态,无法在代码的真实运行过程中再加工。

宏的取消
定义了一个宏后,要对宏进行取消,需要使用指令:

#undef macro_name
通常#undef会和#ifdef或#ifndef等指令配合使用。

宏定义的换行连接
有时候宏的定义可能会很长,为了方便阅读,需要把宏的表达式做换行处理,仍以上面定义的宏multi为例可以这么操作:

#define multi(x, y)
x * y
使用\进行换行连接,这样宏定义指令会在下一行的第一个非空白字符处继续。需要注意的是\必须是当前行的最后一个字符,其后必须是回车。

标准预处理宏
C/C++的编译器也支持了大量的标准预处理宏。这里只说下常用的几个,更多的详情资料请点击3.7. Predefined Macros。
需要注意的是不同的编译器相同功能的宏名称可能命名不一样。

FILE
把当前代码源文件的绝对路径表示为一个字符串常量。

FUNCTION
也被命名为__func__,用于该宏所在函数体的函数名称。可以方便调试或异常判断。

LINE
得到当前宏所在的代码的行数,是一个int型。一般和__FUNCTIN__中配合来标识源代码中的什么地方发生了某个事件。

DATE
生成日期的字符串表达式,格式为Mmm dd yyyy,其中Mmm是月份如”Jan”、”Feb”等等。

TIME
提供了包含时间值的字符串,格式是hh:mm:ss。再次强制宏只是在预处理的时间才进行的代码替换,所以这里的时间指的是编译器的运行时间点,而非程序运行时间。一般用这个宏来记录编译器的编译时间点。

代码示例:

int main (int argc, char ** argv) {
printf(“current file name= %s\n”, FILE);
printf(“current function name= %s\n”, FUNCTION);
printf(“current line number= %d\n”, LINE);
printf(“current date= %s\n”, DATE);
printf(“current time= %s\n”, TIME);
return 0;
}
宏展开后的代码为:

int main (int argc, char ** argv) {
printf(“current file name= %s\n”, “macro.c”);
printf(“current function name= %s\n”, FUNCTION);
printf(“current line number= %d\n”, 15);
printf(“current date= %s\n”, “Mar 15 2015”);
printf(“current time= %s\n”, “12:31:35”);
return 0;
}

宏(macro)定义与使用相关推荐

  1. 宏(macro)定义的简介

    一直在嵌入式中混日子,很多知识点被遗忘的差不多了都,最近也是项目setting需要用宏定义来实现不同setting的区分,在makefile里倒是加好了宏,但是逻辑运算到着实有点手生就搜了搜资料,这里 ...

  2. C语言的宏macro的使用

    C's Macro Introduction 1.The Connect Macros: ## 这是一个预处理连接符,这个操作符主要用来将两个符号连接成为一个完整的宏符号.通过下面的代码,可以看到其具 ...

  3. c语言 macro,C/C++中宏/Macro的深入讲解

    前言 宏(Macro)本质上就是代码片段,通过别名来使用.在编译前的预处理中,宏会被替换为真实所指代的代码片段,即下图中 Preprocessor 处理的部分. C/C++ 代码编译过程 - 图片来自 ...

  4. 宏的定义和调用,输出字符串

    宏的定义和调用,输出字符串 data segment string db "hello,bad boy",0ah,0dh,'$' data ends code segment ma ...

  5. C++中宏的定义与用法(现已被内联函数所代替)

    在noip中,宏还是被经常采用,所以这里讲一下,C++中宏的定义与用法 第一种用法--配合条件编译:#define DEBUG 定义一个叫DEBUG的标识符.它应该与#ifdef或#ifndef配合使 ...

  6. warning:4005 DXGI_STATUS_OCCLUDED,宏重定义

    最近新装的VS2012,发现D3D11程序普遍出现大量的warning:C4005:DXGI_STATUS_OCCLUDED,宏重定义 参考网上的解决方案轻松解决(Click Me): 在项目属性-- ...

  7. 宏重定义 头问题重定义解决办法

    头问题件重定义: 头文件重复包含的问题往往是重定义的问题.下面我们有两种方式解决头文件的重复包含:一个是条件编译的#ifndef...#endif 和 #pragma once. 例如采用: #ifn ...

  8. 关于宏重复定义的问题分析

    前言 在项目中,遇到一个问题,发现一个文件里有2个同样的宏名,并且替代的值相同,可能是前面不小心多复制了一次导致: 但是这里也值得深思和考虑,如果在一个项目中,不小心定义2个相同宏名,但是值不一样的现 ...

  9. warning C4005]ws2def.h(91): warning C4005: “AF_IPX”: 宏重定义 winsock.h(460) : 参见“AF_IPX”的前一个定义

    [问题描述] 在编译socket相关代码时,提示下面这样的错误(这只是其中第一行错误): ws2def.h(91): warning C4005: "AF_IPX": 宏重定义: ...

最新文章

  1. pandas使用groupby函数计算dataframe数据中每个分组的N个数值的滚动标准差(rolling std)、例如,计算某公司的多个店铺每N天(5天)的滚动销售额标准差
  2. [vue] vue如果想扩展某个现有的组件时,怎么做呢?
  3. Jelinek-Merer与Absolute discounting 平滑方法
  4. 基于Android的智能家居手持终端系统开发 毕业论文-A
  5. flask使用sqlit3的两种方式
  6. 用Html5制作的一款数学教学程序Function Graphics(绘制函数图的程序)
  7. ArcGIS/ArcMAP操作录屏视频及相关实验数据(行政界线、地名点、道路路网、水系、乡镇/街道面等)
  8. [iOS]分析Mach-O文件
  9. 麦子学院深度学习视频课程(中文授课、代码讲解为主)
  10. 申请德国农工大学计算机案例,德州农工大学本科案例
  11. matlab 自激振荡,基于Simulink的非线性系统自激振荡的仿真
  12. python数据收集整理教案_数据收集整理教学设计
  13. 李博轩担任摄影作品有哪些?
  14. 高速公路交警的经验之谈[转自QQ群]
  15. Git拉取代码报密码错误
  16. C# 把时间转为秒_Python基础学习笔记(六)日期与时间
  17. 一些常用的sql命令记录
  18. IBM 成立 | 历史上的今天
  19. c 语言计算器带括号优先级,C++结合QT实现带有优先级的计算器功能
  20. 线性表之顺序表基本操作(C语言实现,详细注释版)

热门文章

  1. 中山一院——新一代的智慧医院建设,以流量分析为抓手,提升用户体验
  2. 2019云栖大会丨数字冰雹诠释行业大数据可视决策
  3. python字符串倒数第三个_Python3-字符串的最后一个字符
  4. 互联网思维,发现身边的痛点和痒点
  5. OpenLayers基础教程——地图交互之绘制图形
  6. NYOJ818ZOJ--1037---Gridland
  7. 同一片区域网里实现共享文件夹的方法
  8. vs qt error: rc.exe 、ucrt.lib 、 ucrtd.lib windows sdk 版本
  9. 视频监控分屏简单例子
  10. 外汇优势 炒外汇优势 外汇保证金交易的优点有哪些?