用可变参数扩展printf
2017-06-16 16:51
162 查看
VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件
va_list,定义为字符串指针:
VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
因为第一个参数是固定的,第一个参数的地址加上4(一个地址的长度),就等于第二个参数的地址。如果要深究,就要研究C语言参数入栈相关知识,参数压栈的方向是从右往左,这里简单提下。
VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数,实际应用中经常用格式化函数vsprintf代替VA_ARG宏。
VA_END宏,清空va_list可变参数列表:
下面展示一个简单的程序流程
有了上面基础知识,再来看看printf的一种实现
从上面的程序中可以看出,buf保存着格式化后的数据,系统再调用write函数输出到某一个地方。其实跟我们上一个例子非常相似,不同之处是输出到哪里。我们在调试程序的时候,需要用打印log出来帮我们解决bug。有了上面的知识,我们可以打造一个方便自己调试程序的函数。如下面的例子。
因为实际项目是很庞大的,我们经常需要定位到哪个文件,哪个函数,哪一行出问题了。所以,我们可以用
既然讨论到这里了,我们再试试看怎么把log按照你的意图在终端显示出来。在linux系统中,我们可以通过设置printk的打印级别来控制log,在linux应用程序中,我们又是怎么实现类似的机制呢?
好,下面来讨论分析一下如何在应用中控制log的输出。
我们假设一个情景,一个长期运行的Linux程序,想在不退出运行的情况下,通过某种机制,可以让程序知道要不要打印出log
最简单的实现方式可以这样:
1、创建一个文件,写进标志位
2、然后每次要打印log之前先读取这个文件,按照标志位是什么来决定要不要打印log
这样子我们在linux系统上,如果不想打印出这些log,可以向这个文件写其他标志位。
代码示例如下:
在终端实验,
编译:
运行应用程序:(默认打开log)
关闭log:
打开log:
上面这种做法,实践学习还可以,但是应用到项目中,效率太低,不建议使用。本节先介绍比较低效的方法,然后在接下来的博客中会继续思考如何改进这些问题。在分析存在的问题之后再思考如何改进这些问题的方法,有助于加深对改进机智的理解,同时也能更深刻的学习总结知识。
#include <stdarg.h>,用于获取不确定个数的参数。首先,我们来看看C语言中是如何定义。先看其中比较简单的一种定义,分析完一种,其他实现也可触类旁通。
va_list,定义为字符串指针:
typedef char * va_list;
VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数):
#define va_start(ap,v) ( ap = (va_list)&v + 4 )
因为第一个参数是固定的,第一个参数的地址加上4(一个地址的长度),就等于第二个参数的地址。如果要深究,就要研究C语言参数入栈相关知识,参数压栈的方向是从右往左,这里简单提下。
VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数,实际应用中经常用格式化函数vsprintf代替VA_ARG宏。
VA_END宏,清空va_list可变参数列表:
#define va_end(ap) ( ap = (va_list)0 )
下面展示一个简单的程序流程
char buffer[80] = {0}; int cnt; va_list argptr; //初始化va_list变量argptr va_start(argptr, fmt); //获取可变参数列表的第一个参数的地址argptr cnt = vsprintf(buffer, fmt, argptr);//格式化输出数组buffer va_end(argptr);//清空va_list可变参数列表
有了上面基础知识,再来看看printf的一种实现
int printf(const char *fmt, ...) { int i; char buf[256]; va_list arg = (va_list)((char*)(&fmt) + 4);// i = vsprintf(buf, fmt, arg); write(buf, i); return i; }
从上面的程序中可以看出,buf保存着格式化后的数据,系统再调用write函数输出到某一个地方。其实跟我们上一个例子非常相似,不同之处是输出到哪里。我们在调试程序的时候,需要用打印log出来帮我们解决bug。有了上面的知识,我们可以打造一个方便自己调试程序的函数。如下面的例子。
void printf_my_log(char *file, int line, const char *func, char *fmt, ...) { char argvStr[1024] = {0}; va_list argptr; memset(&argptr, 0, sizeof(va_list)); printf("[%s(Line=%d):%s]",file,line,func); va_start (argptr, fmt); vsprintf(argvStr, fmt, argptr); va_end (argptr); printf("%s\n", argvStr); }
因为实际项目是很庞大的,我们经常需要定位到哪个文件,哪个函数,哪一行出问题了。所以,我们可以用
__FILE__,__LINE__,__FUNCTION__这几个宏来简单扩展一下printf的功能。像上面的例子,我们可以这样调用:
printf_my_log(__FILE__,__LINE__,__FUNCTION__,"%d %f %s", a,b,c);
既然讨论到这里了,我们再试试看怎么把log按照你的意图在终端显示出来。在linux系统中,我们可以通过设置printk的打印级别来控制log,在linux应用程序中,我们又是怎么实现类似的机制呢?
好,下面来讨论分析一下如何在应用中控制log的输出。
我们假设一个情景,一个长期运行的Linux程序,想在不退出运行的情况下,通过某种机制,可以让程序知道要不要打印出log
最简单的实现方式可以这样:
1、创建一个文件,写进标志位
2、然后每次要打印log之前先读取这个文件,按照标志位是什么来决定要不要打印log
这样子我们在linux系统上,如果不想打印出这些log,可以向这个文件写其他标志位。
代码示例如下:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdarg.h> void printf_my_log(char *file, int line, const char *func, char *fmt, ...) { int fd; char filename[] = "printlog"; char readbuf[8] = {0}; int read_count = 0; int logFlag = 0; //打开文件printlog,如果文件不存在,则创建新文件 if((fd = open(filename,O_RDWR|O_CREAT, 0777))<0) { perror("open"); } //读取文件一个字符到readbuf数组中 read_count = read(fd,readbuf,1); //把字符串转化为数字。 logFlag = atoi(readbuf); //没有向文件写入数据或者读取到的标志位为1,则打印log if (read_count < 1 || logFlag == 1) { char argvStr[1024] = {0}; va_list argptr; printf("[%s(Line=%d):%s]",file,line,func); va_start (argptr, fmt); vsprintf(argvStr, fmt, argptr); va_end (argptr); printf("%s\n", argvStr); } close(fd); } int main(void) { int a = 10; printf_my_log(__FILE__,__LINE__,__FUNCTION__,"%d",a); return 0; }
在终端实验,
编译:
ubuntu:~/test/69test$ gcc creatfile.c -o creatfile ubuntu:~/test/69test$ ls creatfile creatfile.c printlog
运行应用程序:(默认打开log)
ubuntu:~/test/69test$ ./creatfile [creatfile.c(Line=54):main]10
关闭log:
ubuntu:~/test/69test$ echo 0 > printlog ubuntu:~/test/69test$ ./creatfile
打开log:
ubuntu:~/test/69test$ echo 1 > printlog
ubuntu:~/test/69test$ ./creatfile [creatfile.c(Line=54):main]10
上面这种做法,实践学习还可以,但是应用到项目中,效率太低,不建议使用。本节先介绍比较低效的方法,然后在接下来的博客中会继续思考如何改进这些问题。在分析存在的问题之后再思考如何改进这些问题的方法,有助于加深对改进机智的理解,同时也能更深刻的学习总结知识。
相关文章推荐
- 一个类似printf的可变参数函数
- 可变参数模拟实现printf
- 从printf谈可变参数函数的实现
- 从printf谈可变参数函数的实现
- 创建函数利用可变参数列表的形式模拟实现printf的功能
- 从printf谈可变参数函数的实现
- [置顶] Printf可变参数使用
- C语言之利用可变参数列表实现简易的printf
- apue读书笔记【二】:处理可变参数(2) vsnprintf
- printf 的实现及 可变参数函数研究
- 可变参数列表的剖析以及printf函数的实现
- C语言中的可变参数-printf的实现原理
- 转:从printf谈可变参数函数的实现
- 从printf谈可变参数函数的实现[转载]
- 使用未公开关键字在 C# 中导入外部 printf 等参数数量可变函数 [1] CLR 支持
- printf函数可变参数是如何实现的?
- 详解C语言可变参数va_list和vsnprintf及printf实现
- 可变参数列表:简单printf函数的实现
- 【C/C++】利用可变参数模拟实现printf
- 从printf谈可变参数函数的实现