zoukankan      html  css  js  c++  java
  • 可变参数宏

    参考一
    参考二
    参考三
    参考四

    一、 format、...、__VA_ARGS__、args、stdout
    二、 自适应参数——##
    三、 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__argsstdout

    printf()fprintf()这些输出函数的参数是可变的,在调试程序时,你可能希望定义自己的参数可变的输出函数,
    那么可变参数宏会是一个选择。
    C99中规定宏可以像函数一样带有可变参数,比如
    #define LOG(format, ...) fprintf(stdout, format, __VA_ARGS__)
    GCC中同时支持如下的形式
    #define LOG(format, args...) fprintf(stdout, format, args)

    其用法和上面的基本一致,只是参数符号有变化
    代码解析:

    • ...代表参数可变
    • format单词是格式的意思,实际操作中会自动根据参数的类型选择像样的格式进行输出
    • stdoutfprintf函数的第一个参数,表示将数据输入到的地方是标准输出台
    • __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在输出管理上也很方便,
    比如调试时输出调试信息,正式发布时则不输出,可以这样

    1. #ifdef DEBUG
    2. #define LOG(format, ...) fprintf(stdout, ">> "format" ", ##__VA_ARGS__)
    3. #else
    4. #define LOG(format, ...)
    5. #endif

    在调试环境下,LOG宏是一个变参输出宏,以自定义的格式输出;
    在发布环境下,LOG宏是一个空宏,不做任何事情。
    这里引用一下MTK中TRACE调试的讲解,了解为什么下面的宏要定义为空:

    种种迹象和从理论上看来,TRACE和MMI_ASSERT是调试的好帮手,但在发布软件时,带上了这个会引来不必要的麻烦.MMI_ASSERT增加了系统重启的频率.TRACE增加了系统的ROM,RAM和CPU的开销.在工作中,我们曾经发现一款手机,由于ROM过于紧张,添加几条TRACE就会出现编译错误,去掉TRACE就编译通过了,导致出了BUG调试十分的麻烦.如何写一种使用时可以TRACE错误,不使用时又不占用系统资源的TRACE呢,我见许多人这样处理,因为NULL会被编译器优化点,后面括号变成一个表达式了.表达式对系统的开销自然小于函数了.

    1. #ifdef DEBUG_KAL
    2. #define MYTRACE(...) kal_prompt_trace(MOD_WAP, __VA_ARGS__)
    3. #else
    4. #define MYTRACE NULL
    5. #endif

    三、 MTK中的用法,以及__FILE____LINE____FUNCTION__

    自定义调试信息的输出(编者语:应该是MTK编程中的写法,做个参考就行)

    1. #define CMMB_LOG(format, ...) mmi_trace("[CMMB] "format" File:%s, Line:%d, Func:%s" , ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__);
    2. #define CMMB_LOG(type, fmt, args...) __android_log_print(type, LOG_TAG, fmt, args)
    3. #define CMMB_INF(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "[%s](%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
    4. #define CMMB_ERR(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "[%s](%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
    5. #define CMMB_WRN(fmt, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "[%s](%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
    1. __VA_ARGS__是一个可变参数的宏,宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错。
    2. __FILE__ 宏在预编译时会替换成当前的源文件名
    3. __LINE__宏在预编译时会替换成当前的行号
    4. __FUNCTION__宏在预编译时会替换成当前的函数名称

    四、 上机实践

    系统Ubuntu 16.04GCC 5.4.0
    记录:

    • 是fprintf,不是printf
    • 每个参数都要带"",所以参数没办法是变量暂时没发现,有待验证
    • format不能带""
    • 参数之间以""间隔
    • 除了__VA_ARGS__宏之外,还可以使用其他的宏,如__FILE__, __LINE__, __FUNCTION__
    1. #include <stdio.h>
    2. #define LOG(format, ...) printf("fomat, File:%s, Line:%d, Func:%s ", ##__VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
    3. #define LOG1(format, ...) fprintf(stdout, format, ##__VA_ARGS__)
    4. #define LOG2(format, ...) printf(format, ##__VA_ARGS__)
    5. #define LOG3(format, ...) printf(format "%s %d %s ", ##__VA_ARGS__, __FILE__, __LINE__, __FUNCTION__)
    6. int main(void)
    7. {
    8. int a = 123;
    9. LOG1("a" " ");
    10. LOG2("b" " ");
    11. LOG3("c" " ");
    12. return 0;
    13. }


    输出结果:

    五、 总结

    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

    结构

    模型

    高速公路





  • 相关阅读:
    第八周上机作业
    第七次作业
    第七周上机
    第六周作业
    4.9上机作业
    第五周作业
    第四周作业
    第二次上机作业
    第三周作业
    第九周上机练习
  • 原文地址:https://www.cnblogs.com/fjutacm/p/327ddd1d96ca419e31431487125d1284.html
Copyright © 2011-2022 走看看