C语言可变参数函数的编写
1. 引言
C语言我们接触的第一个库函数是
printf(“hello,world!”);其参数个数为1个。
然后,我们会接触到诸如:
printf(“a=%d,b=%s,c=%c”,a,b,c);此时其参数个数为4个。
在linux下,输入man 3 printf,可以看到prinf函数原型如下:
SYNOPSIS
#include <stdio.h>
int printf(const char *format, ...);
后面的三个点...表示printf参数个数是不定的.
如何实现可变参数函数?
2. 编写可变函数的素材准备
为了编写可变参数函数,我们需要用到<stdarg.h>头文件下定义的以下函数:
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
其中:
va_list是用于存放参数列表的数据结构。
va_start函数根据初始化last来初始化参数列表。
va_arg函数用于从参数列表中取出一个参数,参数类型由type指定。
va_copy函数用于复制参数列表。
va_end函数执行清理参数列表的工作。
函数官方说明,如果你看到英文就烦,可以自行忽略以下说明。
va_start()
The va_start() macro initializes ap for subsequent use by va_arg() and
va_end(), and must be called first.
The argument last is the name of the last argument before the variable
argument list, that is, the last argument of which the calling function
knows the type.
Because the address of this argument may be used in the va_start()
macro, it should not be declared as a register variable, or as a func‐
tion or an array type.
va_arg()
The va_arg() macro expands to an expression that has the type and value
of the next argument in the call. The argument ap is the va_list ap
initialized by va_start(). Each call to va_arg() modifies ap so that
the next call returns the next argument. The argument type is a type
name specified so that the type of a pointer to an object that has the
specified type can be obtained simply by adding a * to type.
The first use of the va_arg() macro after that of the va_start() macro
returns the argument after last. Successive invocations return the
values of the remaining arguments.
If there is no next argument, or if type is not compatible with the
type of the actual next argument (as promoted according to the default
argument promotions), random errors will occur.
If ap is passed to a function that uses va_arg(ap,type) then the value
of ap is undefined after the return of that function.
va_end()
Each invocation of va_start() must be matched by a corresponding invo‐
cation of va_end() in the same function. After the call va_end(ap) the
variable ap is undefined. Multiple traversals of the list, each brack‐
eted by va_start() and va_end() are possible. va_end() may be a macro
or a function.
GNU给出的一个实例:
#include <stdio.h>
#include <stdarg.h>
void
foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch (*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
说明:
va_start(ap, fmt);用于根据fmt初始化可变参数列表。
va_arg(ap, char *);用于从参数列表中取出一个参数,其中的char *用于指定所取的参数的类型为字符串。每次调用va_arg后,参数列表ap都会被更改,以使得下次调用时能得到下一个参数。
va_end(ap);用于对参数列表进行一些清理工作。调用完va_end后,ap便不再有效。
以上程序给了我们一个实现printf函数的是思路,即:通过调用va_start函数,来得到参数列表,然后我们一个个取出参数来进行输出即可。
例如:对于printf(“a=%d,b=%s,c=%c”,a,b,c)语句;fmt的值为a=%d,b=%s,c=%c,调用va_start函数将参数a,b,c存入了ap中。注意到:fmt中的%为特殊字符,紧跟%后的参数指明了参数类型.
因此我们的简易printf函数如下:
#include <stdio.h>
#include <stdarg.h>
void
myprintf(char *fmt, ...)
{
va_list ap;
int d;
double f;
char c;
char *s;
char flag;
va_start(ap,fmt);
while (*fmt){
flag=*fmt++;
if(flag!='%'){
putchar(flag);
continue;
}
flag=*fmt++;//记得后移一位
switch (flag)
{
case 's':
s=va_arg(ap,char*);
printf("%s",s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("%d", d);
break;
case 'f': /* double*/
d = va_arg(ap,double);
printf("%d", d);
break;
case 'c': /* char*/
c = (char)va_arg(ap,int);
printf("%c", c);
break;
default:
putchar(flag);
break;
}
}
va_end(ap);
}
int main(){
char str[10]="linuxcode";
int i=1024;
double f=3.1415926;
char c='V';
myprintf("string is:%s,int is:%d,double is:%f,char is :%c",str,i,f,c);
}
int sum(int cnt,...){
int sum=0;
int i;
va_list ap;
va_start(ap,cnt);
for(i=0;i<cnt;++i)
sum+=va_arg(ap,int);
va_end(ap);
return sum;
}
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
C语言可变参数函数的编写的更多相关文章
- C语言可变参数函数详解示例
先看代码 printf(“hello,world!”);其参数个数为1个. printf(“a=%d,b=%s,c=%c”,a,b,c);其参数个数为4个. 如何编写可变参数函数呢?我们首先来看看pr ...
- C语言可变参数函数实现原理
一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本 ...
- c语言可变参数函数
c语言支持可变参数函数.这里的可变指,函数的参数个数可变. 其原理是,一般情况下,函数参数传递时,其压栈顺序是从右向左,栈在虚拟内存中的增长方向是从上往下.所以,对于一个函数调用 func(int a ...
- C语言中可变参数函数实现原理
C函数调用的栈结构 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈.例如,对于函数: void fu ...
- C语言中的可变参数函数
C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为: int printf( const char* format, ...); 它除了有一个参数format固定以外 ...
- [11 Go语言基础-可变参数函数]
[11 Go语言基础-可变参数函数] 可变参数函数 什么是可变参数函数 可变参数函数是一种参数个数可变的函数. 语法 如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最 ...
- C语言学习020:可变参数函数
顾名思义,可变参数函数就是参数数量可变的函数,即函数的参数数量是不确定的,比如方法getnumbertotal()我们即可以传递一个参数,也可以传递5个.6个参数 #include <stdio ...
- 转:C语言 可变参数
C语言 可变参数 堆栈一般是怎么压栈处理的 /* * stack space: * * 参数3 | up * 参数2 | * 参数1 v ...
- C可变参数函数 实现
转自:http://blog.csdn.net/weiwangchao_/article/details/4857567 C函数要在程序中用到以下这些宏: void va_start( va_list ...
随机推荐
- Extjs4.1MVC详细解释
:http://xiebaochun.github.io/ app.js [javascript] view plaincopyprint? Ext.onReady(function(){ Ext.Q ...
- MongoDB详解学习历程
MongoDB是一个基于分布式文件存储的数据库,它是介于关系数据库和非关系数据库之间的产品. MongoDB支持的数据结构非常松散,类似json的bjson格式,因此可以存储比较复杂的数据类型.Mon ...
- 2014鞍山直播比赛H称号HDU5077(DFS修剪+通过计)
NAND Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others) Total Sub ...
- TRILL浅析
1 TRILL概述 TRILL的全称就是Transparent Interconnection of Lots of Links,顾名思义,其本质就是将非常多条链路透明地组织在一起,以致于上层IP应用 ...
- 开始 space viking 之旅
设备 cocos2d-v2 眼下cocos2d-v3也不太稳定,它在很大程度上仍然是变化的功能. 对于稳定.我们仍然使用 v2 wget -c http://cocos2d-iphone.goo ...
- 山寨游戏的未来Apple App Store
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXF1c2hp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/d ...
- C#中如何获取系统环境变量
原文:C#中如何获取系统环境变量 C#中获取系统环境变量需要用到Environment Class.其中提供了有关当前环境和平台的信息以及操作它们的方法.该类不能被继承. 以下代码得到%systemd ...
- tomcat的webapps文件夹下放更新后的项目就訪问不了
昨天给同事更新完程序,同事说更新后的程序訪问不了.它曾经的程序叫tj52,更新后的程序叫webapp.也就是tomcat的文件夹有两个文件架,一个叫webapp,一个叫tj52.最后另外一同事给了解决 ...
- 关于Android开发中导出jar包后的资源使用问题解决
我们经常遇到一个需求,就是给别人使用我们工程的时候,为了能够屏蔽代码,把代码封装成jar包提供给第三方使用,但是这样我们的资源文件怎么给对方用呢? 其实并不用这么的复杂,下面就介绍一下具体的方法 一, ...
- POJ 1028 Web Navigation 题解
考查代码能力的题目.也能够说是算法水题,呵呵. 推荐新手练习代码能力. 要添加难度就使用纯C实现一下stack,那么就有点难度了,能够使用数组模拟环形栈.做多了,我就直接使用STL了. #includ ...