可变参数列表及printf函数的实现
来源:互联网 发布:网络布线验收标准 编辑:程序博客网 时间:2024/06/06 04:02
原文:http://blog.chinaunix.net/uid-23215128-id-2521483.html
一直没有搞懂printf函数是怎么实现的,今天又看了一下可变参数列表的函数的编写,大概的了解了一点。反推出原来没想到的一个东西,那就是函数形参列表中的变量在内存中的位置是顺次排列的。
头文件Stdarg.h里的几个宏定义就是利用了这么一点,顺次获取多个参数,感觉还是挺笨的一个方法。
先看一个最简单的可变参数列表的函数:
void mytest(int a, ...)
{
int b;
va_list ap;
va_start(ap, a);
b = va_arg(ap, int);
va_end(ap);
printf("%d, %d", a, b);
}
函数实现打印数字。关键问题是搞懂va_list,va_start,va_arg,va_end几个的意思。
首先是在ADS下写的程序,打开stdarg是这个样子:
typedef int *va_list[1];
#define va_start(ap, parmN) (void)(*(ap) = __va_start(parmN))
#define va_arg(ap, type) __va_arg(*(ap), type)
#define va_end(ap) ((void)(*(ap) = 0))
实在找不到__va_start,__va_arg是怎么实现的,只好到keil下面去找,这里的就简单多了:
typedef char *va_list;
#define va_start(ap,v) ap = (va_list)&v + sizeof(v)
#define va_arg(ap,t) (((t *)ap)++[0])
#define va_end(ap)
原来每个平台下面的Stdarg头文件的定义都是不相同的。就拿keil那里的来入手。
1. 首先定义一个va_list型的变量ap,也就是char *。
2. va_start(ap, a)宏替换之后就是ap = (va_list)&a + sizeof(a);
首先取a的地址,即第一个固定参数的地址,然后强制类型转换为va_list,接着后移a的内存大小,把当前这个地址值赋给ap。很明显,就是第一个参数a后面的那个地方,按照上面说的,也就是第一个可变参数。即现在把ap指向第一个可变参数。
3. b = va_arg(ap, int)宏替换为b = ((int *)ap)++[0];
自加在后,因此是获取第一个参数的值赋给b,然后ap后移一个类型的位置,即指向下一个元素的地址。
4. va_end(ap),这里什么都没有做,在ADS那个版本里是将ap指向NULL,防止误操作。
那么按照这种做法,当用mytest(100, 200);调用这个函数时,它将可以打印出100,200。
如果用mytest(100,200,300);调用这个函数,仍然只是显示100,200。如果要将300打印出来,那么得再使用一次va_arg宏,把它赋值给变量c,把c打印出来。
所以stdarg里的几个宏只是机械的把每个参数读出来,它甚至不知道现在读的是什么类型的变量,如果要读下一个变量的话得往后跳多少位置,这些都只能靠程序自己来判断指示。
所以printf里的格式符号"%d,%s,%c"之类的都是人为规定的,得自己编程识别这些格式。尝试写一个相对简单的prinrf函数:
再看linux下的实现:
void printf(const char *format, ...)
{
va_list args;
int n;
static char xmon_outbuf[1024];
va_start(args, format);
n = vsnprintf(xmon_outbuf, sizeof(xmon_outbuf), format, args);
va_end(args);
xmon_write(xmon_outbuf, n);
}
由此可见,其其实只是先越过format,然后依次取出参数列表给vsnprintf。
一直没有搞懂printf函数是怎么实现的,今天又看了一下可变参数列表的函数的编写,大概的了解了一点。反推出原来没想到的一个东西,那就是函数形参列表中的变量在内存中的位置是顺次排列的。
头文件Stdarg.h里的几个宏定义就是利用了这么一点,顺次获取多个参数,感觉还是挺笨的一个方法。
先看一个最简单的可变参数列表的函数:
void mytest(int a, ...)
{
int b;
va_list ap;
va_start(ap, a);
b = va_arg(ap, int);
va_end(ap);
printf("%d, %d", a, b);
}
函数实现打印数字。关键问题是搞懂va_list,va_start,va_arg,va_end几个的意思。
首先是在ADS下写的程序,打开stdarg是这个样子:
typedef int *va_list[1];
#define va_start(ap, parmN) (void)(*(ap) = __va_start(parmN))
#define va_arg(ap, type) __va_arg(*(ap), type)
#define va_end(ap) ((void)(*(ap) = 0))
实在找不到__va_start,__va_arg是怎么实现的,只好到keil下面去找,这里的就简单多了:
typedef char *va_list;
#define va_start(ap,v) ap = (va_list)&v + sizeof(v)
#define va_arg(ap,t) (((t *)ap)++[0])
#define va_end(ap)
原来每个平台下面的Stdarg头文件的定义都是不相同的。就拿keil那里的来入手。
1. 首先定义一个va_list型的变量ap,也就是char *。
2. va_start(ap, a)宏替换之后就是ap = (va_list)&a + sizeof(a);
首先取a的地址,即第一个固定参数的地址,然后强制类型转换为va_list,接着后移a的内存大小,把当前这个地址值赋给ap。很明显,就是第一个参数a后面的那个地方,按照上面说的,也就是第一个可变参数。即现在把ap指向第一个可变参数。
3. b = va_arg(ap, int)宏替换为b = ((int *)ap)++[0];
自加在后,因此是获取第一个参数的值赋给b,然后ap后移一个类型的位置,即指向下一个元素的地址。
4. va_end(ap),这里什么都没有做,在ADS那个版本里是将ap指向NULL,防止误操作。
那么按照这种做法,当用mytest(100, 200);调用这个函数时,它将可以打印出100,200。
如果用mytest(100,200,300);调用这个函数,仍然只是显示100,200。如果要将300打印出来,那么得再使用一次va_arg宏,把它赋值给变量c,把c打印出来。
所以stdarg里的几个宏只是机械的把每个参数读出来,它甚至不知道现在读的是什么类型的变量,如果要读下一个变量的话得往后跳多少位置,这些都只能靠程序自己来判断指示。
所以printf里的格式符号"%d,%s,%c"之类的都是人为规定的,得自己编程识别这些格式。尝试写一个相对简单的prinrf函数:
再看linux下的实现:
void printf(const char *format, ...)
{
va_list args;
int n;
static char xmon_outbuf[1024];
va_start(args, format);
n = vsnprintf(xmon_outbuf, sizeof(xmon_outbuf), format, args);
va_end(args);
xmon_write(xmon_outbuf, n);
}
由此可见,其其实只是先越过format,然后依次取出参数列表给vsnprintf。
0 0
- 可变参数列表及printf函数的实现
- 可变参数列表及printf函数的实现
- 可变参数列表及printf函数的实现
- 可变参数列表及printf函数的实现
- 可变参数列表及printf函数的实现
- C语言可变参数列表详述及实现printf函数
- C语言可变参数列表详述及实现printf函数
- 可变参数列表的剖析以及printf函数的实现
- 可变参数列表:简单printf函数的实现
- printf谈可变参数函数的实现
- printf函数可变参数的实现
- 【C语言】用可变参数列表实现printf函数
- 用可变参数列表模拟实现printf函数
- 模拟实现printf函数,可变参数列表实例
- 可变参数:printf函数实现
- 【c语言】用可变参数列表实现一个简化的printf函数
- 【C语言】printf函数的简单实现(可变参数列表)
- 【C语言】模拟实现printf函数 可变参数列表的实战
- 【远程Service】AIDL进程间通信实例
- codeforces 474D Flowers dp
- nyoj112指数运算
- MyReport报表引擎2.7.6.7新功能
- JDK8中Java调用Javascript脚本引擎动态定义与执行代码
- 可变参数列表及printf函数的实现
- fopen()函数对文件IO操作时的问题
- android PreferenceActivity类实现设置界面
- erlang工作前新手学习指引路线
- Socket的3次握手链接与4次断开握手
- 恢复SQLSERVER被误删除的数据
- mysql定位执行效率低的sql语句
- 51单片机串口通信使用类似printf函数的两种办法
- Table Javasc DOM操作(2) 具体实例应用