宏定义之二(连接符,可变参数,文件名,行号,函数名)

2023-10-05 16:42

本文主要是介绍宏定义之二(连接符,可变参数,文件名,行号,函数名),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

宏的高级使用--##,__VA_ARGS__, __FILE__, __FUNCTION__等

转自 http://blog.csdn.net/yiya1989/article/details/7849588


先说一下本文中会提到的内容:##,__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__等
宏变量:
先举一个例子,会用到上面这些宏:
[cpp]  view plain copy
  1. #define myprintf(...) printk("[lch]:File:%s, Line:%d, Function:%s," \  
  2.      __VA_ARGS__, __FILE__, __LINE__ ,__FUNCTION__);  

此处的 #define 的作用是将 myprintf( )换成后面那一大串的内容,而括号内 ... 的内容原样抄写在 __VA_ARGS__ 的位置。最终输出如下:

[lch]:File:arch/arm/mach-omap2/board-omap3wscec-camera.c, Line:163, Function:beagle_cam_init,camera init!

解析:

1)__VA_ARGS__:总体来说就是将左边宏中 ... 的内容原样抄写在右边 __VA_ARGS__ 所在的位置。它是一个可变参数的宏,是新的C99规范中新增的,目前似乎只有gcc支持(VC从VC2005开始支持)。要注意的是,printf 的输出格式是括号内左边是字符串,右边是变量,而且右变量与左输出格式是一一对应的。所以在上面那个例子中, __VA_ARGS__只能是一些不含任何变量的字符串常量。因为上面的例子中若__VA_ARGS__含有变量,整个printf的输出与变量便不能一一对应,输出会出错。

如果仅仅是替换函数名,可用如下方式,此时对__VA_ARGS__无任何特殊要求:#define myprintf(...) printk( __VA_ARGS__),在调试程序时可以这样用:

[cpp]  view plain copy
  1. #ifndef LOG_NDEBUG_FUNCTION  
  2. #define LOGFUNC(...) ((void)0)  
  3. #else  
  4. #define LOGFUNC(...) (printk(__VA_ARGS__))  
  5. #endif  
2) __FILE__ :宏在预编译时会替换成当前的源文件名
3) __LINE__:宏在预编译时会替换成当前的行号
4) __FUNCTION__:宏在预编译时会替换成当前的函数名称
5)类似的宏还有 __TIME__,__STDC__, __TIMESTAMP__等,就完全当一个变量来使用即可。

宏连接符##:
举个例子:宏定义为#define XNAME(n) x##n,代码为:XNAME(4),则在预编译时,宏发现XNAME(4)与XNAME(n)匹配,则令 n 为 4,然后将右边的n的内容也变为4,然后将整个XNAME(4)替换为 x##n,亦即 x4,故 最终结果为 XNAME(4) 变为 x4.
代码如下:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #define XNAME(n) x ## n  
  3. #define PRINT_XN(n) printf("x" #n " = %d/n", x ## n);  
  4. int main(void)  
  5. {  
  6. int XNAME(1) = 14; // becomes int x1 = 14;  
  7. int XNAME(2) = 20; // becomes int x2 = 20;  
  8. PRINT_XN(1); // becomes printf("x1 = %d,", x1);  
  9. PRINT_XN(2); // becomes printf("x2 = %d/n", x2);  
  10. return 0;  
  11. }  
输出为:x1 = 14, x2 = 20





PS:编译过程:
1,扫描解析文件
2,预处理(宏在此时处理,该替换的文字会被替换)
3,对处理过的源代码进行汇编,输出汇编语言的代码(C语言的控制流程被处理)
4,编译为二进制目标文件
5,与程序库进行链接,输出最终的程序文件
(宏 和 C语言在不同的阶段处理执行)

这篇关于宏定义之二(连接符,可变参数,文件名,行号,函数名)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/151609

相关文章

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

python中的高阶函数示例详解

《python中的高阶函数示例详解》在Python中,高阶函数是指接受函数作为参数或返回函数作为结果的函数,下面:本文主要介绍python中高阶函数的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录1.定义2.map函数3.filter函数4.reduce函数5.sorted函数6.自定义高阶函数

Python中的sort方法、sorted函数与lambda表达式及用法详解

《Python中的sort方法、sorted函数与lambda表达式及用法详解》文章对比了Python中list.sort()与sorted()函数的区别,指出sort()原地排序返回None,sor... 目录1. sort()方法1.1 sort()方法1.2 基本语法和参数A. reverse参数B.

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧

《Python函数的基本用法、返回值特性、全局变量修改及异常处理技巧》本文将通过实际代码示例,深入讲解Python函数的基本用法、返回值特性、全局变量修改以及异常处理技巧,感兴趣的朋友跟随小编一起看看... 目录一、python函数定义与调用1.1 基本函数定义1.2 函数调用二、函数返回值详解2.1 有返

Python Excel 通用筛选函数的实现

《PythonExcel通用筛选函数的实现》本文主要介绍了PythonExcel通用筛选函数的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录案例目的示例数据假定数据来源是字典优化:通用CSV数据处理函数使用说明使用示例注意事项案例目的第一

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty