C语言添加宏开关
比如,定义一个宏开关:
#define __DEBUG
当需要调试的时候,使用语句:
#ifdef __DEBUG
printf(xxx);
#endif
这种方式的调试,可以通过undef __DEBUG的方式让告知编译器不编译这些语句,从而不再输出这些语句。但是这种方式的麻烦之处也是显而易见的,每一条调试语句都需要使用两条宏定义来包围,这不但在代码的编写上不便,源码结构也不好看,工作量依然不小。
如果我们能够把这三条语句编程一条,那该多舒服呀~,于是,我们想到使用这样的语句:
#ifdef __DEBUG
#define DEBUG(info) printf(info)
#else
#define DEBUG(info)
#endif
这样,我们在编写代码的时候,使用DEBUG一条语句就可以了,我们把宏开关__DEBUG打开,所有的DEBUG(info)宏定义信息都会被替换为printf(info),关上则会被替换成空,因此不会被编译。嗯,这次方便多了,一条语句就可以了~~~ 但是,问题也随之而来了,printf是支持多个参数的,而且是不定参数,当你使用下面这样的语句时就会报错:
DEBUG("%s",msg)
这是因为,DEBUG(info)这条宏定义只支持一个参数的替换。
因此,我们希望DEBUG能够像printf那样,支持多个参数,并且这些参数刚好展开成为printf语句本身使用的参数,譬如:我们希望DEBUG("%s",msg)能够展开为printf("%s",msg)
正文:
通过网上的资料查阅,发现自C99规范以后,编译器就开始支持不定参数的宏定义,就像printf一样。
大家可以看看这篇文章:http://blog.csdn.net/aobai219/archive/2010/12/22/6092292.aspx
(这个链接也转的,我已经找不到原始作者到底是谁了,唉,互联网啊。。。)
于是,我们定义了一个这样的东东:
#define DEBUG(format, ...) printf (format, ##__VA_ARGS__)(' ## '的意思是,如果可变参数被忽略或为空,将使预处理器( preprocessor )去除掉它前面的那个逗号。)
于是乎,我们神奇地发现,DEBUG完全取代了printf,所有的DEBUG(…)都被完成的替换成了printf(…),再也不会因那个可恶的逗号而烦恼了。
但是,我们发现,光有printf还不够,虽然调试信息是输出了,可是很多的调试信息输出,我们并不能一下子知道这条信息到底是在那里打印出来的,于是,我们又想,能不能把当前所在文件名和源码行位置也打印出来呢,这样不就一目了然了吗,哪里还用的着去想,去找调试信息在哪里输出的呢,都已经打印出来了!
于是我们就有了下面的故事。。。
编译器内置宏:
先介绍几个编译器内置的宏定义,这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息。
ANSI C标准中有几个标准预定义宏(也是常用的):
__LINE__:在源代码中插入当前源代码行号;
__FILE__:在源文件中插入当前源文件名;
__DATE__:在源文件中插入当前的编译日期
__TIME__:在源文件中插入当前编译时间;
__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。
编译器在进行源码编译的时候,会自动将这些宏替换为相应内容。
看到这里,你的眼睛应该一亮了吧,嗯,是的,__FILE__和__LINE__正是我们前面想要的输出的,于是,我们的每一条语句都变成了:
DEBUG("FILE: %s, LINE: %d…",__FILE__,__LINE__,…)
其实没有必要,__FILE__本身就会被编译器置换为字符常量,于是乎我们的语句又变成了这样:
DEBUG("FILE:"__FILE__", LINE: %d…",__LINE__,…)
但是,我们还是不满足,依然发现,还是很讨厌,为什么每条语句都要写"FILE:"__FILE__", LINE: %d 以及,__LINE,这两个部分呢?这不是浪费我们时间么?
哈哈,是的,这就是本次大结局,把DEBUG写成这样:
DEBUG(format,...) printf("FILE: "__FILE__", LINE: %d: "format"/n", __LINE__, ##__VA_ARGS__)
没错,就是这样!下面,所有的DEBUG信息都会按照这样的方式输出:
FILE: xxx, LINE: xxx, …….
最后:
最后,老规矩,coding测试。
//============================================================================
// Name : debug.cpp
// Author : boyce
// Version : 1.0
// Copyright : pku
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <stdio.h> #define __DEBUG__ #ifdef __DEBUG__
#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"\n", __LINE__, ##__VA_ARGS__)
#else
#define DEBUG(format,...)
#endif int main(int argc, char **argv) {
char str[]="Hello World";
DEBUG("A ha, check me: %s",str);
return ;
}
测试结果:

C语言添加宏开关的更多相关文章
- Android.bp 添加宏开关【转】
本文转载自:https://github.com/zzb2760715357/document/blob/master/android_doc/Android.bp%E6%B7%BB%E5%8A%A0 ...
- 如何为C语言添加一个对象系统
为C语言添加OO能力的尝试从上世纪70年代到现在一直没有停止过,除了大获成的C++/Objective-C以外,还有很多其它的成功案例,比如GTK在libg中实现了一个对象系统,还有前几年一个OOC, ...
- Linux学习笔记——举例说,makefile 添加宏定义
0.前言 从学习C语言開始就慢慢開始接触makefile,查阅了非常多的makefile的资料但总感觉没有真正掌握makefile.假设自己动手写一个makefile总认为非常吃力. 所以特意 ...
- Makefile如何通过宏开关进行条件编译
在开发中经常会遇到需要条件编译一段代码,即: #ifdef DEBUG { 如果定义了DUBUG,则执行此段代码!} #else {否则执行此段代码!} 这就需要通过宏开关来进行条件编译,也就是常说的 ...
- C语言中宏定义(#define)时do{}while(0)的价值(转)
C语言中宏定义(#define)时do{}while(0)的价值 最近在新公司的代码中发现到处用到do{...}while(0),google了一下,发现Stack Overflow上早有很多讨论,总 ...
- c 语言中宏定义和定义全局变量的区别
宏定义和定义全局变量的区别: 1 作用时间不同. 宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以. 2 本质类型不同. 宏定义的只是一段字符,在编译的时候被替换到引用的位置.在运行中是没有 ...
- 注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果
注解是建立在class文件基础上的东西,同C语言的宏有异曲同工的效果 https://www.cnblogs.com/deman/p/5519901.html @是java注解,即annotation ...
- Android源码分析(十三)----SystemUI下拉状态栏如何添加快捷开关
一:如何添加快捷开关 源码路径:frameworks/base/packages/SystemUI/res/values/config.xml 添加headset快捷开关,参考如下修改. Index: ...
- node-gyp 在此解决方案中一次生成一个项目。若要启用并行生成,请添加“/m”开关。
在此解决方案中一次生成一个项目.若要启用并行生成,请添加“/m”开关. MSBUILD : error MSB3428: 未能加载 Visual C++ 组件“VCBuild.exe”.要解决此问题, ...
随机推荐
- linux——Shell编程基础
1. shell 脚本的执行方式 1.1 直接绝对路径执行 1.2 相对路径执行 首先进入到shell脚本所造的目录 PS:用./执行要增加x权限.用bash执行可以不增加x权限 1.3 在当前she ...
- 20155217 2016-2017-2 《Java程序设计》第2周学习总结
20155217 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 Java中的基本类型主要可分为整数.字节.浮点数.字符与布尔. 整数可分为short整数(占 ...
- 20155232 实验二《Java面向对象程序设计》实验报告
20155232 实验一<Java面向对象程序设计>实验报告 实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4.熟悉S.O ...
- 20155320 2016-2017-2《Java程序设计》课程总结
20155320 2016-2017-2<Java程序设计>课程总结 (按顺序)每周作业链接汇总 预备作业1:第一次写随笔,回答了老师的一些问题,写下了期望和目标 预备作业2:总结了一下自 ...
- easyui-dialog打开多次数据串台问题
问题: 公司项目最近在用easyui写后台管理系统,发现dialog使用后总会报出莫名奇妙的问题 1.打开多次后数据显示上一次的,表单提交错误或者获取数据错误 2.来回跳转后,会有打开其它dialog ...
- QT-2D编程
QT-[转]2D编程 Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕上和绘图·设备上进行绘制,主要基于QPainter.QPainterDevice和QPainterEngine这3个类. ...
- 修改表的字段顺序(mysql)
ALTER TABLE 表名 CHANGE 字段名 字段名 int not null default 1 AFTER 它前面的字段;
- Spring学习(三)-----Spring自动装配Beans
在Spring框架,可以用 auto-wiring 功能会自动装配Bean.要启用它,只需要在 <bean>定义“autowire”属性. <bean id="custom ...
- 【Pthon入门学习】多级菜单小例子
menu_list = { '北京':{ '昌平':{ '回龙观':{ '和谐家园':{}, '矩阵小区':{}, '北店家园':{} }, '沙河':{ '北街家园1区':{}, '北街家园2区': ...
- Amazon.com 购物 信用卡预售期
I understand and thanks for confirming. In this case, the $1.00 is not a charge. It is an authoriza ...