什么是可变参数
在C语言编程中,我们会遇到一些参数数量可变的函数,如printf() scanf()……
函数原型为
int printf(const char* format,…)
int scanf(const char *format,…)
在这些函数中,除了参数format固定外,还存在"..."占位符,后面的参数类型与数量都是可变的
含可变参数的函数参数列表
在任何一个含可变参数的函数中,它们都有两个部分:
一是固定参数,至少要有一个固定参数,其声明与普通函数参数声明相同;
另一个是可选参数,可选参数由于数目不定(0个或以上),声明时用"…"表示。
固定参数和可选参数共同构成可变参数函数的参数列表。
怎么实现可变参数
C语言中使用va_list系列变参宏实现变参函数,此处va意为variable-argument(可变参数)。
<stdarg.h>
//
// stdarg.h
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <stdarg.h> header.
//
#pragma once
#define _INC_STDARG
#include <vcruntime.h>
#pragma warning(push)
#pragma warning(disable: _VCRUNTIME_DISABLED_WARNINGS)
_CRT_BEGIN_C_HEADER
#define va_start __crt_va_start
#define va_arg __crt_va_arg
#define va_end __crt_va_end
#define va_copy(destination, source) ((destination) = (source))
_CRT_END_C_HEADER
#pragma warning(pop) // _VCRUNTIME_DISABLED_WARNINGS
typedef char * va_list;
// 把 n 变为 sizeof(int) 的倍数
#define _INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1) )
// 初始化 ap 指针,使其指向第一个可变参数。v 是可变参数列表的前一个参数
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
// 该宏返回当前可变参数值,并使 ap 指向列表中的下个可变参数
#define va_arg(ap, type) ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )
// 将指针 ap 置为无效,结束可变参数的获取
#define va_end(ap) ( ap = (va_list)0 )
代码示例
#include <stdarg.h>
#include <stdio.h>
int sum(int n, ...)//n为参数个数
{
int i, sum = 0;
va_list vap; //定义参数列表,vap为可变参数类型
va_start(vap, n); //初始化参数列表
for (i = 0; i < n; i++)
{
sum += va_arg(vap, int);//va_arg()获取 n 后面每一个参数的值; 获取值的类型
}
va_end(vap);//收尾工作
return sum;
}
int main()
{
printf("%d", sum(3, 1, 23, 4));
return 0;
}
注意
va_arg(ap, type)宏中的 type 不可指定为以下类型:
- char
- short
- float
在C语言中,调用不带原型声明或声明为变参的函数时,主调函数会在传递未显式声明的参数前对其执行缺省参数提升(default argument promotions),将提升后的参数值传递给被调函数。
提升操作如下:
- float 类型的参数提升为 double 类型
- char、short 和相应的 signed、unsigned 类型参数提升为 int 类型
- 若 int 类型不能容纳原值,则提升为 unsigned int 类型