天天看點

C語言-函數的可變形參(不定形參)

1. 前言

在學習C語言函數章節時發現,給函數傳入的形參必須和函數定義原型的類型、數量一緻才可以正常調用。

平時使用的

printf

scanf

等函數時,傳入的參數數量卻可以随意改變,例如:

printf("大家好");
printf("我是整數:%d\n",123);
printf("%d%d%d%d\n",1,2,3,4);
printf("%s%s%s\n","1","2","3","4");           

printf

函數是如何實作這種傳參方式的?

我們看一下

printf,scanf

系列函數的原型。

#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);           

發現這些函數定義時,參數清單裡有一個省略符号

...

,這個省略符号就表示目前函數支援

不定長形參

示例代碼:可變形參的聲明方式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void func(char *p,...);
int main(int argc,char **argv)
{
    func("123",1,2,3,4,"",12.345);
    return 0;
}

//正确的
void func(char *p,...)
{
    
}

//錯誤的
void func2(...,char *p)
{
    
}

//錯誤的
void func3(...)
{
    
}           

2. 可變形參本身實作原理

明白了如何定義可變形參,接下來就得學習可變形參的原理,然後學習如何去提取這些傳入的參數。

(1). 函數的形參是放在棧空間的。

(2). 可變形參,傳入的多餘的參數都是存放在棧空間。

存放記憶體位址是連續的。

理論上隻要知道傳入參數的首位址,就可以推出其他參數的位址。

系統的标準參數頭檔案和處理可變形參的相關函數

#include <stdarg.h>
int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char  *str,  size_t  size,  const  char  *format,va_list ap);

直接檢視頭檔案的幫助:
[wbyq@wbyq linux_c]$ man stdarg.h
void va_start(va_list ap, argN);   //開始
void va_copy(va_list dest, va_list src); //拷貝
type va_arg(va_list ap, type);  //取具體形參—取值
void va_end(va_list ap);  //結束

va_list ap; 就是定義一個char類型的指針。va_list==char *           

3. 單獨提取參數清單裡的值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void foo(char *fmt, ...);
int main(int argc,char **argv)
{
    foo("%d,%s,%c",12,"123",'A');
    return 0;
}

// foo("%d,%s,%c",12,"123",'A')
void foo(char *fmt, ...)
{
   va_list ap;  //定義一個char類型指針
   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 */
           c = (char) va_arg(ap, int);
           printf("char %c\n", c);
           break;
       }
   va_end(ap); //将ap指針置為NULL
}           

4. 使用格式化方式提取形參清單裡的值

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void foo(char *fmt, ...);
int main(int argc,char **argv)
{
    foo("int=%d,string=%s char=%c",12,"123",'A');
    return 0;
}

// foo("%d,%s,%c",12,"123",'A')
void foo(char *fmt, ...)
{
   char buff[100];
   va_list ap;  //定義一個char類型指針
   va_start(ap, fmt); //指針位址指派--初始化
   //将參數清單裡所有參數,按照格式化轉換成字元串-存放到str指向的空間
   vsprintf(buff,fmt,ap);
   va_end(ap); //将ap指針置為NULL
   
   printf("%s\n",buff);
}           

5. 提取可變形參清單裡的單個資料

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void foo(char *fmt, ...);
int main(int argc,char **argv)
{
    foo("sdcf","hello",666,'A',123.456);
    return 0;
}

void foo(char *fmt, ...)
{
   va_list ap;  //定義一個char類型指針
   int d;
   char c, *s;
   double f;
   
   va_start(ap, fmt); //指針位址指派--初始化
   while(*fmt) //周遊fmt指針指向空間的值
   {
         switch(*fmt++) 
        {
            case 's':              /* string */
               s = va_arg(ap, char *);
               printf("字元串:%s\n", s);
               break;
            case 'd':              /* int */
               d = va_arg(ap, int);
               printf("整型:%d\n", d);
               break;
            case 'c':              /* char */
               c = (char) va_arg(ap,int);
               printf("字元:%c\n", c);
               break;
            case 'f':              /* float */
               f = va_arg(ap, double);
               printf("浮點數:%f\n", f);
               break;
        }
   }
   va_end(ap); //将ap指針置為NULL
}           

6. 精簡代碼-提取可變形參清單裡的單個資料

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void foo(char *fmt, ...);
int main(int argc,char **argv)
{
    foo("123","hello",666,'A',123.456);
    return 0;
}

void foo(char *fmt, ...)
{
   va_list ap;  //定義一個char類型指針
   va_start(ap, fmt); //指針位址指派--初始化
   printf("第一個字元串:%s\n",fmt);
   printf("提取字元串:%s\n",va_arg(ap,char*));
   printf("提取整數:%d\n",va_arg(ap,int));
   printf("提取字元:%c\n",va_arg(ap,int));
   printf("提取字元:%lf\n",va_arg(ap,double));
   va_end(ap); //将ap指針置為NULL
}           

繼續閱讀