可变参数列表解析及简单应用:
在函数原型中,列出函数期望接受的参数,但是原型只能显示固定数目的参数。通俗来讲就是,当我们给定函数原型时候,我们也就确定了函数的参数的个数,传递参数的时候必须按照原型提供的参数个数来传递参数。
那么我们是否可以传递参数时候,提供可变个参数呢?
而va_start、va_arg、va_end经过宏定义为_crt_va_start、_crt_va_arg、_crt_va_end.
为什么要定义为_crt函数呢?首先我们得了解什么是CRT函数,一般来说,CRT函数就是标准的C语言函数。而可变参数列表是标准库的一部分。
为了对宏定义进行分析说明,我们将宏定义全部替换可得到如下:
先来解释此处的宏定义的作用:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。
比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。
~(sizeof(int) - 1) )就应该为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) - 1) )后最后两位肯定为0,就肯定是4的整数倍了。
(sizeof(n) + sizeof(int) - 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) - 1) )后就正好将原长度补齐到4的倍数了。
用可变参数列表打印。“hellow”"bit".
那么我们是否可以传递参数时候,提供可变个参数呢?
当然是可以的,比如我们常用的printf()函数,我们可以用以下方式传递多个参数给此函数。
printf("hello");//一个参数
printf("%d",12);//两个参数
printf("%d%d",12,11);//三个参数
printf函数原型。
可以看到在printf函数参数中有由 “…”组成的参数构成,这是可变函数参数的标准格式,也是标志。我们把参数可变的函数叫做可变参数函数。
可变参数列表是通过宏来实现的,这些宏定义在头文件stdarg.h中,这个头文件中声明了一个类型va_lsit和三个宏——va_start、va_arg、va_end。
解析:
va_list类型:这个类型是通过typedef将char 类型进行了重命名,也就是说va_list是char 类型。
va_start宏:用来初始化,它的第一个参数是va_list变量的名字,第二个参数是省略号前最后一个用名字的参数。初始化过程是把va_list类型的变量设置为指向可变参数部分的第一个参数。
va_arg宏:为了访问参数,需要使用这个宏来实现,这个宏接受两个参数,va_list变量和下一个参数的类型。
va_end宏:使用这个宏来结束访问。
以下是简单实现用可变参数列表来求不同个数参数的平均值。
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
float average(int num, ...)
{
va_list arg;//声明va_list类型变量arg
int count;
float sum = 0;
va_start(var_arg, num);//初始化变量
for (count = 0; count < num; count += 1)
{
sum += va_arg(arg, int);//访问变量
}
va_end(arg);//调用结束
return sum / num;
}
int main()
{
float aver = 0.0;
aver=average(5, 1, 2, 3, 4, 5);
printf("%f\n", aver);
system("pause");
return 0;
以下用可变参数列表求最大值。
#include<stdio.h>
#include<stdarg.h>
#include<stdlib.h>
int fun(int num, ...)
{
va_list arg;
int i = 0;
int max = 0;
int tmp = 0;
va_start(arg, num);
max = va_arg(arg, int);
for (i = 0; i < num - 1; i++)
{
tmp = va_arg(arg, int);
if (tmp>max)
{
max = tmp;
}
}
va_end(arg);
return max;
}
int main()
{
int max = 0;
max = fun(5, 1, 2, 3, 4, 5);
printf("%d\n",max);
system("pause");
return 0;
}
可以看到va_list类型实质是char *类型。
而va_start、va_arg、va_end经过宏定义为_crt_va_start、_crt_va_arg、_crt_va_end.
为什么要定义为_crt函数呢?首先我们得了解什么是CRT函数,一般来说,CRT函数就是标准的C语言函数。而可变参数列表是标准库的一部分。
为了对宏定义进行分析说明,我们将宏定义全部替换可得到如下:
先来解释此处的宏定义的作用:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。
比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。
~(sizeof(int) - 1) )就应该为~(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) - 1) )后最后两位肯定为0,就肯定是4的整数倍了。
(sizeof(n) + sizeof(int) - 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) - 1) )后就正好将原长度补齐到4的倍数了。
用可变参数列表打印。“hellow”"bit".
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#include <string.h>
int m_printf(const char *str, ...)
{
char *eos = (char *)str;
int ret = 0;
va_list ap;
va_start(ap, str);
assert(str);
while (*eos)
{
if('%' == *eos)
{
int d = 0;
char c = 0;
char *s = NULL;
eos++;
switch(*eos)
{
case 's':
s = va_arg(ap, char*);
while(*s)
{
putchar(*s);
s++;
ret++;
}
//不能用puts()->会自动换行
//不能用putchar()->会只输出首字母
break;
case 'c':
// c = va_arg(ap, char);
// printf("%c", c);
putchar(va_arg(ap, char));
ret++;
break;
case 'd':
d = va_arg(ap, int);
printf("%d", d);
while(d)
{
ret++;
d /= 10;
}
break;
case '\0':
putchar('\0');
default :
;
break;
}
}
else
{
putchar(*eos);
ret++;
}
eos++;
}
va_end(ap);
return (ret);
}
int main()
{
char str1[] = "Hello";
char str2[] = "bit";
int num = 200;
int ret1 = m_printf("第%d行 : %s %c%c%c\n", 100, "hello",'b','i','t');
int ret2 = m_printf("第%d行 : %s %s\n", num, str1, str2);
//求解:中文为什么能以%c格式打印出来,而不是%s
//m_printf(NULL);
//试验:当传入空指针时。
m_printf("ret1 = %d\n",ret1);
m_printf("ret2 = %d\n",ret2);
return 0;
}