可变参数列表与printf()函数的实现
问题
当我们刚开始学习C语言的时候,就接触到printf()函数,可是当时“道行”不深或许不够细心留意,又或者我们理所当然地认为库函数规定这样就是这样,没有发现这个函数与普通的函数存在区别,普通函数的参数在函数定义的时候就确定,而printf()函数的参数列表在调用时可变。还有一个原因导致我们没有去关注这个函数的实现,就是在编程的过程中很少用到参数列表可变的函数。的确是这样的,但是如果可以理解并内化,这将在编程过程中对某些功能实现带来很大的帮助。比如,在嵌入式设备开发中,可以利用设备的接口(UART、USB)编写出一套类似printf()函数功能的调试工具。
printf()函数的实现
static char sprint_buf[1024];
int printf(char *fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vsprintf(sprint_buf, fmt, args);
va_end(args);
write(stdout, sprint_buf, n);
return n;
}
咋一看,printf()函数的试下也不是太复杂,有几个陌生又关键的词语va_list、va_start、va_end、vsprintf。其实还有一个va_arg(stdarg.h),是参数列表可变的关键,而真正的打印实现是vsprintf()函数,在这里不讨论vsprintf()函数,只讨论三个宏定义。
先看看stdarg.h文件下这三个宏的实现:
第一种:
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)
第二种:
typedef char *va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,type) (*(type *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
_INTSIZEOF(n):为了字节对齐,将n的长度化为int长度的整数倍。
在32位系统中,~(sizeof(int)–1) )展开为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int)–1) )后最后两位肯定为0,也就是4的整数倍。
每个平台下的stdarg.h头文件的定义都不相同,但是意思都一样:
(1) va_list: 定义一个va_list型的变量ap,也就是char *;
(2) va_start:获取到可变参数表的首地址,并将该地址赋给指针ap;
(3) va_arg: 获取当前ap所指向的可变参数,并将指针ap指向下一个可变参数。注意,该宏的第二个参数为类型;
(4) va_end: 结束可变参数的获取。
应用
当理解了printf()函数的实现,便可以着手去实现嵌入式设备中的调试工具,其原理一样:
int bsp_debug_printf(const char *fmt, ...)
{
int ret = -1;
va_list ap;
char buf[256];
va_start(ap, fmt);
(void)vsnprintf((char *)buf, sizeof(buf), fmt, ap);
va_end(ap);
bsp_puts(buf);
ret = 0;
return ret;
}
以上只是简单的实现这个功能,并没有考虑硬件保护,返回打印字符数等。
再看一例:
找出最大值
int MaxTest(int c, ...)
{
int max = c;
va_list ap;
va_start(ap,c);
c = va_arg(ap,int);
while(0 != c)
{
if(max < c) max = c;
c = va_arg(ap,int);
}
va_end(ap);
return max;
}
这个例子主要想利用va_list、va_start、va_arg、va_end这四个关键指令来实现可变参数列表的函数,它需要一个参数列表末尾标识(0)来告诉系统这参数列表的最后一个参数。
还可以有另外一种方法来实现以上功能:
int MaxTest1(int n, ...)
{
int max = n;
int *p = &n + 1;
while(0 != *p)
{
if (max < *p) max = *p;
p++;
}
return max;
}
这个例子利用了函数参数的存储规则来实现对参数的获取。可变参数函数的实现与函数调用的栈结构有关,正常情况下C/C++的函数参数入栈规则从右到左的,即函数中的最右边的参数最先入栈。
这种方法看似效果相同,但是稍有不慎就会带来灾难。若printf()函数由这种方法实现,而没有边界的检查,当堆栈越界访问时极可能导致程序崩溃。
可变参数列表与printf()函数的实现的更多相关文章
- C语言中可变参数的原理——printf()函数
函数原型: int printf(const char *format[,argument]...) 返 回 值: 成功则返回实际输出的字符数,失败返回-1. 函数说明: 使用过C语言的人所再熟悉不过 ...
- C语言函数可变参数列表
C语言允许使用可变参数列表,我们常用的printf函数即为可变参数函数,C标准库提供了stdarg.h为我们提供了这方面支持:该头文件提供了一些类型和宏来支持可变参数列表,包括类型va_list,宏v ...
- C利用可变参数列表统计一组数的平均值,利用函数形式参数栈原理实现指针运算
//描述:利用可变参数列表统计一组数的平均值 #include <stdarg.h> #include <stdio.h> float average(int num, ... ...
- PHP函数可变参数列表的具体实现方法介绍
PHP函数可变参数列表可以通过_get_args().func_num_args().func_get_arg()这三个函数来实现.我们下面就对此做了详细的介绍. AD:2014WOT全球软件技术峰会 ...
- php实现函数可变参数列表
使用func_get_args().func_num_args().func_get_arg() 可以构造一个可变参数列表的函数. 首先大致介绍以上三个函数. (1)array func_get_ar ...
- 可变参数列表---以dbg()为例
在UART驱动的drivers/serial/samsung.h中遇到如下定义: #ifdef CONFIG_SERIAL_SAMSUNG_DEBUG extern void printascii(c ...
- tips:可变参数列表
tips:可变参数列表! 先来看看以往我们要传递许多参数时是怎么做的: java: public static void main(String []args){} c: int main(int a ...
- C++可变参数列表处理宏va_list、va_start、va_end的使用
VA_LIST是在C语言中解决变参问题的一组宏他有这么几个成员: 1)va_list型变量: #ifdef _M_ALPHA typedef struct{ char* a0; /* ...
- 【转】C++可变参数列表处理宏va_list、va_start、va_end的使用
VA_LIST是在C语言中解决变参问题的一组宏他有这么几个成员: 1)va_list型变量: #ifdef _M_ALPHA typedef struct{ char* a0; /*po ...
随机推荐
- 30多条mysql数据库优化方法,千万级数据库记录查询轻松解决(转载)
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...
- Yii ActiveRecord 的via和viaTable示例
Yii中,将两个不相关的表利用中间表关联有via和viaTable两种方法,这里通过用户权限查询来进行示例. 关系如上,需要建立三个表 用户表 权限表 用户表 数据: 权限表 数据: 关联表 数据: ...
- 深入理解android:id以及@+id/name和@id/name的区别联系
今天为了好好研究了下@+id/name和@id/name的区别以及联系,又翻了翻文档/guide/topics/resources/layout-resource.html中关于 android:id ...
- Mysql上手
使用Mysql,打开 相应的服务.启动-- 打开命令窗口.此处有多种方法,我是在开始菜单(Mysql5.6 Command Line Client)打开的(简单). mysql -h localhos ...
- SQL分组多列统计(GROUP BY后按条件分列统计)
as tjsl from fyxx group by zt,whbmbh end) as ybhsl from fyxx group by whbmbh 下面是摘自别人的博客 最近遇到一个问题,需要对 ...
- xml编辑器
cstring转cha型方法在mfc中用过可行 int CstringToch(CString str, char *ch) { assert(ch); memset(ch, 0, sizeof(ch ...
- 通过rsync+inotify实现数据的实时备份
我讲到过利用rsync实现数据的镜像和备份,但是要实现数据的实时备份,单独靠rsync还不能实现,本文就讲述下如何实现数据的实时备份. 一.rsync的优点与不足 与传统的cp.tar备份方式相比,r ...
- Python 30分钟入门——数据类型 and 控制结构
Python是一门脚本语言,我也久闻大名,但正真系统的接触学习是在去年(2013)年底到今年(2014)年初的时候.不得不说的是Python的官方文档相当齐全,如果你是在Windows上学习Pytho ...
- PHP新手常见的一些不好习惯(抄的 有待理解)
1.不写注释(是个好习惯,不过也没必要每个语句都要写) 2.不使用可以提高生产效率的IDE工具 3.不使用版本控制 4.不按照编程规范写代码 5.不使用统一的方法 6.编码前不去思考和计划 7.在执行 ...
- 21. Merge Two Sorted Lists —— Python
题目: Merge two sorted linked lists and return it as a new list. The new list should be made by splici ...