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;
}结果:15 和 56 的和 = 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 fieldint precision;    // Min. # of digits for integers; max number of chars for from stringint qualifier;    // 'h', 'l', or 'L' for integer fieldsfor (str = buf; *fmt; fmt++){if (*fmt != '%'){*str++ = *fmt;continue;}// Process flagsflags = 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 widthfield_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 precisionprecision = -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 qualifierqualifier = -1;if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L'){qualifier = *fmt;fmt++;}// Default basebase = 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);elsestr = 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);elsenum = va_arg(args, unsigned short);}else if (flags & SIGN)num = va_arg(args, int);elsenum = 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

C语言传递可变参数详解相关推荐

  1. C可变参数详解及其函数设计

    在stdarg.h文件中有如下几个宏定义: [cpp]view plaincopy #include <vadefs.h> #define va_start _crt_va_start # ...

  2. java 可变参数 详解(通俗易懂)

    目录 一.概述: 二.格式: 三.注意事项(使用规范): 四.代码演示: 演示规范①~③: 演示规范④: 演示规范⑤: 课堂练习: 代码演示: 输出结果: 五.英文版本讲解: 一.概述: java中, ...

  3. c语言main函数参数详解,c/c++程序main函数参数解析,以及参数传递的不同方法

    1. 程序运行时传递参数的不同方法: 1)通过命令参数传递参数: 2) 通过txt文件读取参数. 2. 方法举例 1)通过命令参数传递参数,即解析main函数的参数. main函数可以不带参数,也可以 ...

  4. python可变参数教学,Python函数可变参数详解

    在实际使用函数时,可能会遇到"不知道函数需要接受多少个实参"的情况,不过好在 Python 允许函数从调用语句中收集任意数量的实参. 例如,设计一个制作披萨的函数,我们知道,披萨中 ...

  5. C语言main函数参数详解

    main函数参数 一共有三个: 1.int argc 整型变量 2.char *argv[] 字符指针的数组,通俗一点就是字符串数组,每个元素都是字符串 3.char *envp[] 字符串数组 这三 ...

  6. C语言命令行参数详解

    C语言的main函数通常含有参数argc和argv,写法通常如下: int main(int argc,char *argv[]) int main(int argc,char **argv) 下面详 ...

  7. R语言axis函数参数详解(坐标轴函数)

    首先我们来看一下函数帮助文档所给定的axis函数参数: axis(side, at = NULL, labels = TRUE, tick = TRUE, line = NA,pos = NA, ou ...

  8. c语言 ioctl,ioctl参数详解

    本函数影响由fd参数引用的一个打开的文件. #include int ioctl( int fd, int request, .../* void *arg */ ); 返回0:成功-1:出错 第三个 ...

  9. c语言fopen函数的作用是,C语言 fopen函数 参数详解

    文件打开函数fopenfopen函数用来打开一个文件,其调用的一般形式为: 文件指针名=fopen(文件名,使用文件方式) 其中,"文件指针名"必须是被说明为FILE 类型的指针变 ...

最新文章

  1. Erlang命令行提示符汇总
  2. python安装方法3.8.2_Linux安装Python3.8.1的教程详解
  3. tab vue 竖排_vue 实现tab切换保持数据状态
  4. ue4手机ui_Epic Games工程师分享:如何在移动平台上做UE4的UI优化?
  5. 配电室智能监控系统设计及实现分析-Susie 周
  6. SmartUpload
  7. 【深度学习】卷积神经网络(CNN)原理
  8. Juphoon RTC年度成绩单,请查收!
  9. 2020大学计算机知到答案,2020知到APP大学计算机(北华大学)章节测试答案
  10. Java飞机小游戏代码详解
  11. linux公社_如何在Linux中安装和使用dig和nslookup命令
  12. Keras深度学习实战(37)——手写文字识别
  13. 【数理统计】假设检验
  14. videojs实现视频打点标记 Vue
  15. 机器学习如何提高GPU利用率
  16. HDOJ--1000A + B Problem
  17. Vue3 使用moment进行时间转换
  18. 计算机基础之流水线(七)
  19. Fully-Convolutional Siamese Networks for Object Tracking翻译
  20. PHP网站头部通栏设计

热门文章

  1. 华为鸿蒙这个年度总结,才应该刷爆朋友圈
  2. [网络安全自学篇] 三十九.hack the box渗透之DirBuster扫描路径及Sqlmap高级注入用法(三)
  3. MariaDB存储引擎介绍
  4. 【JAVA】URL转二维码以及图片合成
  5. 数据结构课程设计4:公交线路提示
  6. 【树莓派3】安装Ubuntu Mate系统
  7. CUDA安装失败问题解决(windows)
  8. CUDA安装位置版本查询
  9. 人民日报痛批沉睡中的大学生,你不失业,天理难容
  10. 论文阅读《Bidirectional LSTM-CRF Models for Sequence Tagging》