C語言可變參數函數詳解樣本_C 語言

來源:互聯網
上載者:User

先看代碼

複製代碼 代碼如下:

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執行清理工作。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.