C语言:深度解剖函数的可变参数

我们以一个例子来引入可变参数列表,现在呢,我们需要写一个求平均值的函数,但是不知道求几个数的平均值,你给我几个数,我就求几个数的平均值
也就是说,我们得写出求任意个数的平均值的函数,那么如何设计这个函数呢?下面就来写一下:

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

int average(int n, ...)
{
	va_list arg;
	int sum = 0;
	int i = 0;
	va_start(arg, n);

	for (i = 0; i < n; i++)
	{
		sum += va_arg(arg, int);
	}
	va_end(arg);
	return sum / n;
}
int main()
{
	int ret = average(3, 3, 4, 5);
	printf("%d\n", ret);
	system("pause");
	return 0;
}


分析过程:

int average(int n, ...)  

其中,n叫未知参数列表前第一个有名字的参数
比如,我们写个复杂的int average(int n,int m, ...),此时初始化arg的时候,arg的参数一定传的是m,因为它是跟后面的未知参数紧挨着,拿到m的地址才能找到后面参数列表的位置,所以这个地方的一定是.....前面第一个有名字的参数

  va_list arg;

最终,我们拿arg来维护未知参数列表参数的位置
  va_list是一个类型,而且是对char*类型的重定义,所以 va_list arg;又可以写成

  char *arg;
 int sum = 0;
    int i = 0;
 va_start(arg, n);


   上面 va_start(arg, n);这句的意思是:初始化arg为未知参数列表的第一个参数的地址
    (ap = (va_list)&v + _INTSIZEOF(v);这句话在预处理时替换为下面一行代码
    (arg = (char*)&n + _INTSIZEOF(n))    _INTSIZEOF(n)是向上取整,取四个字节为一个整数,所以又可以简化如下:
    (arg = (char*)&n + 4); //替换之后的结果
//#define va_start(ap,v) (ap=(va_list)&v+_INTSIZEOF(v))

    for (i = 0; i < n; i++)
    {
        sum = va_arg(arg,int);

  sum = va_arg(arg,int); 这句代码可以写成下面所示代码:
        sum += (*(int*)((arg+= 4) - 4));//这里类型不能传错,如果传错类型,会出现不可预料的后果
//#define va_start(ap,t) (*(t*)((ap+=_INTSIZEOF(t))-_INTSIZEOF(t)))
    }

 va_end(arg);

 va_end(arg);是把arg赋值为空指针
    (arg = (char*)0);
#define va_end(arg)  (arg=va_list)0)

    return sum / n;
}
int main()
{
    int ret = average(3, 3, 4, 5);
    printf("%d\n", ret);
    +system("pause");
    return 0;
}

注意:我们是用n来初始化arg,让arg初始化完之后,就是未知参数列表的第一个参数的地址,这里设计的很高效,取地址n,强制类型转换为char*,char*+=4,那么4又是怎么算出来的呢?4是根据int sizeof(n)算出来的,根据n的大小,+4正好跳过一个n,那就是要根据n的大小跳过去的,否则这个地方跳的不合理,va_arg(arg,int)是一个宏,它有两个参数,一个是arg,这是告诉我们,从arg位置开始取,取的类型为int
这段代码最关键的地方:就是(*(int*)((arg+= 4) - 4)),让arg向后走了,同时再-4,留下来的是它没加之前的地址。
当做完这些时,最后va_end(arg);把arg赋值为一个空指针,然后就停下来了

代码练习:求参数部分里面n个参数里面的最大值

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

int Max(int n, ...)
{
    va_list arg;//用来维护未知参数列表每个参数的地址
    int max = 0;//这里的max不能赋值成0,赋成第一个值才比较合理
    int i = 0;
    va_start(arg, n);
    max = va_arg(arg, int);//把第一个值取出来后,后面可以少取一次,不至于拿出第一个再和第一个比较
    for (i = 1; i < n; i++)
    {
        int tmp = va_arg(arg, int);//使用tmp会避免它跳的太快
        if (tmp>max)
        {
            max = tmp;//不要太过频繁的调用arg
        }
    }
    return max;
}
int main()
{

    int max = Max(3, 1, 2, 5);
    printf("max=%d\n", max);
    system("pause");
    return 0;
}

再比如说:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int main()
{
    printf("%d %c %s,100,bit,'q'");
    system("pause");
    return 0;
}

  这个例子就说明,其实可变参数列表的第一个参数不一定是参数个数,像printf函数的第一个参数就不是表示参数的个数。
 

猜你喜欢

转载自blog.csdn.net/qq_42270373/article/details/81172490