C语言传递可变参数详解

C语言传递可变参数详解

1.可变参数详解

在有些情况下,我们需要传递的参数的个数是可变的。C 语言为这种情况提供了一个解决方案,它允许定义一个函数,能根据具体的需求接受可变数量的参数。可以使编程更加的灵活,适应各种要求。

返回类型 函数名(classType, ... ){}

这个和一般的函数并没有什么两样,只是在第二个参数后,利用“…”代替后面跟着不定个参数。

当我们需要获取传入的可变参数时,就需要用到stdarg.h函数库,stdarg.h 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。

va_list这是一个适用于 va_start()va_arg()va_end() 这三个宏存储信息的类型。

va_start()

声明:void va_start(va_list ap, last_arg);

参数:
     ap -- 这是一个 va_list 类型的对象,它用来存储通过 va_arg 获取额外参数时所必需的信息。
    
	 last_arg -- 最后一个传递给函数的已知的固定参数。
    
返回值:无
    
说明:C 库宏 void va_start(va_list ap, last_arg) 初始化 ap 变量,它与 va_arg 和 va_end 宏是一起使用的。last_arg 是最后一个传递给函数的已知的固定参数,即省略号之前的参数。
    
这个宏必须在使用 va_arg 和 va_end 之前被调用。

va_arg()

声明:type va_arg(va_list ap, type);
参数:
	ap -- 这是一个 va_list 类型的对象,存储了有关额外参数和检索状态的信息。该对象应在第一次调用 va_arg 之前通过调用 va_start 进行初始化。
        
	type -- 这是一个类型名称。该类型名称是作为扩展自该宏的表达式的类型来使用的。
        
返回值:该宏返回下一个额外的参数,是一个类型为 type 的表达式。
    
说明:C 库宏 type va_arg(va_list ap, type) 检索函数参数列表中类型为 type 的下一个参数。它无法判断检索到的参数是否是传给函数的最后一个参数。

va_end()

声明:void va_end(va_list ap)

参数:
     ap -- 这是之前由同一函数中的 va_start 初始化的 va_list 对象。
    
返回值:无
    
说明:C 库宏 void va_end(va_list ap) 允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。

例子:

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

int sum(int, ...);

int main()
{
   printf("15 和 56 的和 = %d\n",  sum(2, 15, 56) );
   return 0;
}

int sum(int num_args, ...)
{
   int val = 0;
   va_list ap;
   int i;

   va_start(ap, num_args);
   for(i = 0; i < num_args; i++)
   {
      val += va_arg(ap, int);
   }
   va_end(ap);
 
   return val;
}

结果:1556 的和 = 71

2.printf函数原型定义

printf函数原型

typedef char *va_list;

#define   _AUPBND        (sizeof (acpi_native_int) - 1)
#define   _ADNBND        (sizeof (acpi_native_int) - 1)

#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
#define va_end(ap)    (void) 0
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

//start.c
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;
}

int vsprintf(char *buf, const char *fmt, va_list args)
{
  int len;
  unsigned long num;
  int i, base;
  char *str;
  char *s;

  int flags;            // Flags to number()

  int field_width;    // Width of output field
  int precision;    // Min. # of digits for integers; max number of chars for from string
  int qualifier;    // 'h', 'l', or 'L' for integer fields

  for (str = buf; *fmt; fmt++)
  {
    if (*fmt != '%')
    {
      *str++ = *fmt;
      continue;
    }

    // Process flags
    flags = 0;
repeat:
    fmt++; // This also skips first '%'
    switch (*fmt)
    {
      case '-': flags |= LEFT; goto repeat;
      case '+': flags |= PLUS; goto repeat;
      case ' ': flags |= SPACE; goto repeat;
      case '#': flags |= SPECIAL; goto repeat;
      case '0': flags |= ZEROPAD; goto repeat;
    }

    // Get field width
    field_width = -1;
    if (is_digit(*fmt))
      field_width = skip_atoi(&fmt);
    else if (*fmt == '*')
    {
      fmt++;
      field_width = va_arg(args, int);
      if (field_width < 0)
      {
    field_width = -field_width;
    flags |= LEFT;
      }
    }

    // Get the precision
    precision = -1;
    if (*fmt == '.')
    {
      ++fmt;   
      if (is_digit(*fmt))
        precision = skip_atoi(&fmt);
      else if (*fmt == '*')
      {
        ++fmt;
        precision = va_arg(args, int);
      }
      if (precision < 0) precision = 0;
    }

    // Get the conversion qualifier
    qualifier = -1;
    if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
    {
      qualifier = *fmt;
      fmt++;
    }

    // Default base
    base = 10;

    switch (*fmt)
    {
      case 'c':
    if (!(flags & LEFT)) while (--field_width > 0) *str++ = ' ';
    *str++ = (unsigned char) va_arg(args, int);
    while (--field_width > 0) *str++ = ' ';
    continue;

      case 's':
    s = va_arg(args, char *);
    if (!s)    s = "<NULL>";
    len = strnlen(s, precision);
    if (!(flags & LEFT)) while (len < field_width--) *str++ = ' ';
    for (i = 0; i < len; ++i) *str++ = *s++;
    while (len < field_width--) *str++ = ' ';
    continue;

      case 'p':
    if (field_width == -1)
    {
      field_width = 2 * sizeof(void *);
      flags |= ZEROPAD;
    }
    str = number(str, (unsigned long) va_arg(args, void *), 16, field_width, precision, flags);
    continue;

      case 'n':
    if (qualifier == 'l')
    {
      long *ip = va_arg(args, long *);
      *ip = (str - buf);
    }
    else
    {
      int *ip = va_arg(args, int *);
      *ip = (str - buf);
    }
    continue;

      case 'A':
    flags |= LARGE;

      case 'a':
    if (qualifier == 'l')
      str = eaddr(str, va_arg(args, unsigned char *), field_width, precision, flags);
    else
      str = iaddr(str, va_arg(args, unsigned char *), field_width, precision, flags);
    continue;

      // Integer number formats - set up the flags and "break"
      case 'o':
    base = 8;
    break;

      case 'X':
    flags |= LARGE;

      case 'x':
    base = 16;
    break;

      case 'd':
      case 'i':
    flags |= SIGN;

      case 'u':
    break;

      case 'E':
      case 'G':
      case 'e':
      case 'f':
      case 'g':
        str = flt(str, va_arg(args, double), field_width, precision, *fmt, flags | SIGN);
    continue;

      default:
    if (*fmt != '%') *str++ = '%';
    if (*fmt)
      *str++ = *fmt;
    else
      --fmt;
    continue;
    }

    if (qualifier == 'l')
      num = va_arg(args, unsigned long);
    else if (qualifier == 'h')
    {
      if (flags & SIGN)
    num = va_arg(args, short);
      else
    num = va_arg(args, unsigned short);
    }
    else if (flags & SIGN)
      num = va_arg(args, int);
    else
      num = va_arg(args, unsigned int);

    str = number(str, num, base, field_width, precision, flags);
  }

  *str = '/0';
  return str - buf;
}

3.在程序中写入log文件的方法

#ifndef _LOG_H_
#define _LOG_H_
#include <stdio.h>
#include <stdarg.h>

#define FILE_LOG "log_file"

void hy_log_file(const char *format, ...);

#define HY_LOG(format, ...)			hy_log_file(format, ##__VA_ARGS__)
//VA_ARGS是系统预定义宏,被自动替换为参数列表

static FILE *fd = NULL;
void init_logfile(void)
{	
	fd = fopen(FILE_LOG, "w");	
}
void hy_log_file(const char *format, ...)
{
	va_list args;
	va_start(args, format);
	vfprintf(fd, format, args);
	va_end(args);
	fflush(fd);
	//fclose(fd);
}	
#endif
示例:
#include <stdio.h>
#include <stdlib.h>
#include "log.h"
int main(int argc, char *argv[]) {
	init_logfile();
	HY_LOG("this is %d\n", 2);
	HY_LOG("this is %d\n", 3);
	HY_LOG("this is %d\n", 4);
	return 0;
}

结果:
this is 2
this is 3
this is 4
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值