C语言可变参数函数详解示例

先看代码


代码如下:

printf(“hello,world!”);其参数个数为1个。
printf(“a=%d,b=%s,c=%c”,a,b,c);其参数个数为4个。

如何编写可变参数函数呢?我们首先来看看printf函数原型是如何定义的。
在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函数执行清理参数列表的工作。
上述函数通常用宏来实现,例如标准ANSI形式下,这些宏的定义是:


代码如下:

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,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )

使用宏_INTSIZEOF是为了按照整数字节对齐指针,因为c调用协议下面,参数入栈都是整数字节(指针或者值)。
函数官方说明,如果你看到英文就烦,可以自行忽略以下说明。
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函数,来得到参数列表,然后我们一个个取出参数来进行输出即可。
3.实例
例如:对于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);
}

从上面我们可以知道可变参数函数的编写,必须要传入一个参数fmt,用来告诉我们的函数怎样去确定参数的个数。我们的可变参数函数是通过自己解析这个参数来确定函数参数个数的。
比如,我们编写一个求和函数,其函数实现如下:


代码如下:

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;
}

总结一下就是:通过va_start初始化参数列表(也就能得到具体的参数个数了),然后使用va_arg函数从参数列表中取出你想要的参数,最后调用va_end执行清理工作。

(0)

相关推荐

  • c语言基于stdarg.h的可变参数函数的用法

    C语言编程中有时会遇到一些参数个数可变的函数,本文详细讲解了可变参数函数的实现原理,分享给大家 在开始学习C语言的函数的时候,我们就知道函数的参数个数应该是在函数声明的时候就指定的,这一点我们没有任何疑问.但是不知道大家有没有注意到我们的printf()函数,他的函数参数理论上并不是确定的,而是随着匹配字符串中的格式控制符的个数控制的.其实当时也没有注意到这一点,到是最近,偶然间看到了 <嗨翻C语言> 这本书,这里就详细讲解了这种可变参数函数的实现原理,今天考试间隙就顺带学习了一下,其实就是一

  • C语言中编写可变参数函数

    通过stdarg.h头文件为函数提供了定义可变参数列表的能力.声明一个可变参数的函数类似: void f1(int n,...); 其中n表示参数列表个数,而用省略号来表示未知参数列表.stdarg.h中提供了一个va_list类型,用于存放参数.一个大概的使用过程类似: void f1(int n,...) { va_list ap; va_start(ap,n); //初始化参数列表 double first=va_arg(ap,double); //取第一个参数 int second=va

  • C语言可变参数函数详解示例

    先看代码 复制代码 代码如下: printf("hello,world!");其参数个数为1个.printf("a=%d,b=%s,c=%c",a,b,c);其参数个数为4个. 如何编写可变参数函数呢?我们首先来看看printf函数原型是如何定义的.在linux下,输入man 3 printf,可以看到prinf函数原型如下: 复制代码 代码如下: SYNOPSIS#include <stdio.h>int printf(const char *form

  • C语言可变参数函数详解

    目录 C语言可变参数函数 总结 C语言可变参数函数 C 语言允许定义参数数量可变的函数,这称为可变参数函数(variadic function).这种函数需要固定数量的强制参数(mandatory argument),后面是数量可变的可选参数(optional argument). 这种函数必须至少有一个强制参数.可选参数的类型可以变化.可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定. C 语言中最常用的可变参数函数例子是 printf()和 scanf().这两个函数都

  • Swift如何调用Objective-C的可变参数函数详解

    前言 这个问题是一个朋友问我怎么写,一开始我是拒绝的.我想这种东西网上随便 google 下不就有了吗.他说,查了,但没大看明白.于是我就查了下,没想到这个写法确实有点诡异,我第一反应也没看明白.所以随便水一篇文章,强行完成本周的博客任务,顺便给朋友一个交代. 本文分为两部分,第一部分是 Swift 怎么调用 Objective-C 的可变参数函数,第二部分是 Objective-C 怎么调用 Swift 的可变参数函数. Swift 调用 Objective-C 的可变参数函数 先写一个例子

  • Java可变参数列表详解

    Java可变参数列表详解 1.接受的传入参数情况: 如public void test(String ...args){...} 1)不使用参数,如test() 2)使用一个或多个参数,如test("1"); test("1","2"); 3)使用数组 test(new String[]{"1","2"}); 2.方法内部访问参数: 在test方法内部,我们可以像使用数组的访问方式一样来访问参数args.如

  • Java Varargs 可变参数用法详解

    Java1.5 提供了一个叫varargs的新功能,就是可变长度的参数. "Varargs"是 "variable number of arguments"的意思.有时候也被简单的称为 "variable arguments". 定义实参个数可变的方法:只要在一个形参的"类型"与"参数名"之间加上三个连续的"."(即"...",英文里的句中省略号),就可以让它和不确定

  • Go语言中的函数详解

    1.函数的声明定义 //func关键字 //getStudent函数名 //(id int, classId int) 参数列表 //(name string,age int) 返回值列表 func getStudent(id int, classId int)(name string,age int) { //函数体 if id==1&&classId==1{ name = "BigOrange" age = 26 } //返回值 return name, age /

  • C语言之qsort函数详解

    目录 一.qsort函数原型 二.qsort常见的几种比较函数 1.int类型的排序 2.double类型的排序 3.char类型的排序 4.字符串的排序: 1.按首字母排序 2.按字符串长度排序: 总结 一.qsort函数原型 qsort 功 能: 使用快速排序例程进行排序,这个函数是根据二分法写的,其时间复杂度为n*log(n) #include<stdlib.h> void qsort(void *base, int nelem, int width, int (*fcmp)(const

  • C语言内存操作函数详解

    目录 头文件:#include<memory.h> 2.memmove 3.memcmp 4.memset 总结 头文件:#include<memory.h> 1.memcpy 作用:内存拷贝 函数原型: void *memcpy( void *dest, const void *src, size_t count ); 使用: 使用格式:memcpy(目的地,原,想操作内存大小(单位字节)) 把 "参数2" 起始的 "参数3" 个字节 内容

  • C语言动态内存函数详解

    目录 动态开辟空间的原因 1.malloc函数 2.free函数 3.calloc函数 4.realloc函数 总结 动态开辟空间的原因 静态开辟空间是固定的,数组在申明的时候必须指定数组的长度,在编译的时候分配内存,但是我们在实际情况中对于内存空间的需求,不仅仅是上述的情况,有时候我们需要的空间只有在运行之后才能知道,所以需要开辟一个动态内存空间,满足更多需求. 1.malloc函数 void* malloc (size_t size); malloc函数是向内存申请一块连续的空间,并返回指向

  • C语言的可变参数函数实现详解

    目录 1.简介 2.简单的使用方式 总结 1.简介 今天看到一个有趣的东西C语言的可变参数函数 众所周知,C语言的函数不能重载,那么你printf和scanf是怎么可以输入多个参数的 例如查看到的printf的定义为 printf(const char *_Restrict, ...); 这称为可变参数函数.这种函数需要固定数量的强制参数,后面是数量可变的可选参数 这种函数必须至少有一个强制参数.可选参数的类型可以变化.可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定. C

随机推荐