【C语言】可变参数列表剖析

素颜马尾好姑娘i 2022-05-19 23:36 330阅读 0赞

一、为什么函数要有可变参数列表

  1. 就举一个简单的例子来解答这个问题吧,具有一定C语言编程基础的读者,一定知道求两个数平均值的函数,实现过程很简单,我们只需要把两个参数传给函数,并用一个变量接收函数返回来的结果即可。
  2. 但是,我们都知道现实生活中,我们需要求平均值的情况有很多种,比如,当我想要求某个同学期末平均成绩,这个时候可能需要传的参数个数就不是两个了。
  3. 那么对于不同数量的参数,我们如何通过编写一个函数来实现对不同数量的参数进行接收并处理呢?这就要用到可变参数列表了。

二、什么是可变参数列表

  1. 可变参数列表的实现是依靠标准库中stdarg.h里面定义的宏来实现的。在stdarg.h头文件中声明了一个类型va\_list和三个宏——va\_start, va\_argva\_end 通过声明va\_list的变量,与这三个宏的配合使用,可以访问参数的值。
  2. 其中,va\_list的定义为:typedef char \* va\_list,下面是可变参数列表用到的三个宏的定义:
  3. #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
  4. #define va_end(ap) ( ap = (va_list)0 )
  5. #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

其中用到的_INTSIZEOF宏定义如下:

  1. #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
  2. 依次对上面的宏进行分析:
  3. 1)首先说一下\_INTSIZEOF(n),怎么理解这里呢?首先,联系到前面的宏定义 ,我们应该知道这里的n就指的是可变参数的类型,假设在32位的程序中,当可变参数类型为char时,对于二进制数(1+4-1) & ~(4-1)的结果为4,同样的,在32位的程序中,如果可变参数类型是short(占2个字节),int(占4个字节),double(占8个字节), long double(占12个字节)时,它的结果分别是448 12。所以\_INTSIZEOF(n)的功能就是取整到sizeof(int)**。怎么理解取整到n呢?比如n41,2,3,445,6,7,88。**
  4. 2va\_start(ap,v)中 ap = (va\_list)&v + \_INTSIZEOF(v),其中va\_list是一个char \*类型,类比于学习过的内容,就是取出v的地址中的第一个字节,然后加上\_INTSIZEOF(v),并把得到的结果赋给ap
  5. 3va\_arg(ap,t),从ap中取出类型为t的数据,并将指针相应后移。如va\_arg(ap, int)就表示取出一个int数据并将指针向移四个字节。
  6. va\_end(ap)很简单,就是将ap指针置成空指针。
  7. 因此在函数的整个过程就是:先用va\_start()得到变参的起始地址,再用va\_arg()一个一个取值,最后再用va\_end()将指针置为空,说明参数获取完毕,接下来就轮到解析可变参数了。

三、可变参数列表的使用

  1. 需要注意一点,在CC++中无法确定传入的可变参数的个数,所以一般情况下,在使用可变参数列表进行传参时,需要先指定传可变参数的个数。下面的代码中在主函数对Avg函数传参时,第一个参数4就说明了,要传入的可变参数的数量为4。还有一点需要说明一下,printf是根据"%"的个数来确定输入参数的个数的。
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. float Avg(int n, ...) {
  5. va_list arg;
  6. int i = 0;
  7. float sum = 0;
  8. va_start(arg, n);
  9. for (i = 0; i < n; ++i)
  10. {
  11. sum += va_arg(arg, int);
  12. }
  13. va_end(arg);
  14. return sum / n;
  15. }
  16. int main() {
  17. printf("%.2f", Avg(4, 23, 43, 23, 12));
  18. return 0;
  19. }

四、可变参数的限制

  1. 1)可变参数必须从头开始按顺序访问,不可以一开始就访问参数列表中间的值;
  2. 2)参数列表至少包含一个命名参数。如果一个命名参数都没有,无法使用va\_start
  3. 3va\_start, va\_arg, va\_end宏是无法确定实际存在的参数数量以及参数的类型。

发表评论

表情:
评论列表 (有 0 条评论,330人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C语言可变参数

    C语言可变参数可以使用宏函数取出,宏函数在头文件stdarg.h中。 贴出如下简单的代码,博客转载自: [https://www.cnblogs.com/edver/p/84