二、 自适应参数——##
三、 MTK中的用法,以及__FILE__、__LINE__、__FUNCTION__
四、 上机实践
五、 总结
1、 理论+实践=学好编程
六、 整体性学习
1、 获取信息
1. 压缩:format,...,##,__VA_ARGS__
2. 容量:基本宏+函数特性+调试技巧(资源浪费)
3. 速度:在函数中使用format和...,结合宏参数实现可变参数的使用
2、 理解
字面意思:参数可变,format自动适应参数类型/格式
3、 拓展
1. 背景: 编译原理
2. 横向: main的参数;可变参数函数的实现;断言;__LINE__等宏参数
3. 纵向: 太极拳中,拳义理是不变的,招式是可变的;射箭中,背肌撒放的原理是不变的,但是撒放的收发是可变的;内核链表中,指针的存储和指向是不变的,但是指针至上的数据区是可以存放任意类型的数据。
4、 纠错
宏的本质是替换,这点任何时候都不能变/忘
参数可变,要结合函数本身的特性,不能牛唇不对马嘴
可变参数宏的写法很灵活,并不是只能使用fprintf,或者printf
参数列表可以为空,但是需要##
宏可以为空宏
5、 应用
可以使用这个编写自己的printf函数,加上宏开关语句,可以很方便的在调试和发布之前切换,而不需要自己手动评比调试语句
6、 测试
IFACE300代码中使用
#ifdef判断的是后面的宏有没有宏定义,而不论该宏定义为0还是非0
结构
模型
高速公路
一、 format
、...
、__VA_ARGS__
、args
、stdout
printf()
和fprintf()
这些输出函数的参数是可变的,在调试程序时,你可能希望定义自己的参数可变的输出函数,
那么可变参数宏会是一个选择。
C99中规定宏可以像函数一样带有可变参数,比如
#define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__)
GCC中同时支持如下的形式
#define LOG(format, args...) fprintf(stdout, format, args)
其用法和上面的基本一致,只是参数符号有变化
代码解析:
...
代表参数可变format
单词是格式的意思,实际操作中会自动根据参数的类型选择像样的格式进行输出stdout
是fprintf
函数的第一个参数,表示将数据输入到的地方是标准输出台__VA_ARGS__
是编译预处理中会被实际输入的参数内容所替代
同上,可以推理出用printf
做可变参数宏的写法:
#define LOG(format, ...) printf(format, __VA_ARGS__)
二、 自适应参数——##
有一点需要注意,上述的宏定义不能省略可变参数,尽管你可以传递一个空参数,这里有必要提到##
连接符号的用法。
##
的作用是对token
进行连接,在上例中,format、__VA_ARGS__、args
即是token
,
如果token
为空,那么不进行连接,所以允许省略可变参数(__VA_ARGS__
和args
),对上述变参宏做如下修改
#define LOG(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
#define LOG(format, args...) fprintf(stdout, format, ##args)
#define LOG(format, ...) printf(format, ##__VA_ARGS__)
上述的变参宏定义不仅能自定义输出格式,而且配合#ifdef #else #endif
在输出管理上也很方便,
比如调试时输出调试信息,正式发布时则不输出,可以这样
#ifdef DEBUG
#define LOG(format, ...) fprintf(stdout, ">> "format" ", ##__VA_ARGS__)
#else
#define LOG(format, ...)
#endif
在调试环境下,LOG宏是一个变参输出宏,以自定义的格式输出;
在发布环境下,LOG宏是一个空宏,不做任何事情。
这里引用一下MTK中TRACE调试的讲解,了解为什么下面的宏要定义为空:
种种迹象和从理论上看来,TRACE和MMI_ASSERT是调试的好帮手,但在发布软件时,带上了这个会引来不必要的麻烦.MMI_ASSERT增加了系统重启的频率.TRACE增加了系统的ROM,RAM和CPU的开销.在工作中,我们曾经发现一款手机,由于ROM过于紧张,添加几条TRACE就会出现编译错误,去掉TRACE就编译通过了,导致出了BUG调试十分的麻烦.如何写一种使用时可以TRACE错误,不使用时又不占用系统资源的TRACE呢,我见许多人这样处理,因为NULL会被编译器优化点,后面括号变成一个表达式了.表达式对系统的开销自然小于函数了.
#ifdef DEBUG_KAL
#define MYTRACE(...) kal_prompt_trace(MOD_WAP, __VA_ARGS__)
#else
#define MYTRACE NULL
#endif
三、 MTK中的用法,以及__FILE__
、__LINE__
、__FUNCTION__
自定义调试信息的输出(编者语:应该是MTK编程中的写法,做个参考就行)
#define CMMB_LOG(format, ...) mmi_trace("[CMMB] "format" File:%s, Line:%d, Func:%s" , ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__);
#define CMMB_LOG(type, fmt, args...) __android_log_print(type, LOG_TAG, fmt, args)
#define CMMB_INF(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "[%s](%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define CMMB_ERR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "[%s](%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define CMMB_WRN(fmt, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "[%s](%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
__VA_ARGS__
是一个可变参数的宏,宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错。__FILE__
宏在预编译时会替换成当前的源文件名__LINE__
宏在预编译时会替换成当前的行号__FUNCTION__
宏在预编译时会替换成当前的函数名称
四、 上机实践
系统Ubuntu 16.04
,GCC 5.4.0
记录:
是fprintf,不是printf- 每个参数都要带
""
,所以参数没办法是变量暂时没发现,有待验证 format
不能带""
- 参数之间以
""
间隔 - 除了
__VA_ARGS__
宏之外,还可以使用其他的宏,如__FILE__, __LINE__, __FUNCTION__
等
#include <stdio.h>
#define LOG(format, ...) printf("fomat, File:%s, Line:%d, Func:%s ", ##__VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
#define LOG1(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
#define LOG2(format, ...) printf(format, ##__VA_ARGS__)
#define LOG3(format, ...) printf(format "%s %d %s ", ##__VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
int main(void)
{
int a = 123;
LOG1("a" " ");
LOG2("b" " ");
LOG3("c" " ");
return 0;
}
输出结果:
五、 总结
1、 理论+实践=学好编程
开发中会遇到一些新问题或者说已经模糊了的旧知识,对于新问题我们一般都是百度,看到讲解明白后就行了,顶多就是总结记录一下,但是实际效果并不明显,过不了多久遇到同样的问题,还是要百度查阅资料,这也可能是因为我记忆力不怎么出众的原因,不能一概而论。但是有这个问题的应该不少,我想想觉得问题的关键就是:到目前为止,所做的工作只单纯的设计到理论、概念,并没有亲自动手按照教程进行实践操作。
就像《如何高效学习》中提到的:只是获取了信息,还没有应用,更没有纠错,知识怎么能记得牢、用的好、正确无误呢?
=====================================随笔部分=================================================
六、 整体性学习
1、 获取信息
1. 压缩:format
,...
,##
,__VA_ARGS__
2. 容量:基本宏+函数特性+调试技巧(资源浪费)
3. 速度:在函数中使用format
和...
,结合宏参数实现可变参数的使用
2、 理解
字面意思:参数可变,format
自动适应参数类型/格式
3、 拓展
1. 背景: 编译原理
2. 横向: main
的参数;可变参数函数的实现;断言;__LINE__
等宏参数
3. 纵向: 太极拳中,拳义理是不变的,招式是可变的;射箭中,背肌撒放的原理是不变的,但是撒放的收发是可变的;内核链表中,指针的存储和指向是不变的,但是指针至上的数据区是可以存放任意类型的数据。
4、 纠错
宏的本质是替换,这点任何时候都不能变/忘
参数可变,要结合函数本身的特性,不能牛唇不对马嘴
可变参数宏的写法很灵活,并不是只能使用fprintf
,或者printf
参数列表可以为空,但是需要##
宏可以为空宏
5、 应用
可以使用这个编写自己的printf
函数,加上宏开关
语句,可以很方便的在调试和发布之前切换,而不需要自己手动评比调试语句
6、 测试
IFACE300代码中使用
#ifdef
判断的是后面的宏有没有宏定义,而不论该宏定义为0还是非0
结构
模型
高速公路
format
,...
,##
,__VA_ARGS__
format
和...
,结合宏参数实现可变参数的使用format
自动适应参数类型/格式main
的参数;可变参数函数的实现;断言;__LINE__
等宏参数