c语言可变参数

2023-06-01    https://www.cnblogs.com/NJ-Leon/

 

一、函数

有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。

C 语言为这种情况提供了一个解决方案,它允许您定义一个函数,能根据具体的需求接受可变数量的参数。

声明方式为:

int func_name(int arg1, ...);

 其中,省略号 ... 表示可变参数列表。

下面的实例演示了这种函数的使用:

复制代码
int func(int, ... )  {
   .
   .
   .
}
 
int main() {
   func(2, 2, 3);
   func(3, 2, 3, 4);
}
复制代码

请注意,函数 func() 最后一个参数写成省略号,即三个点号(...),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:

  • 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
  • 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
  • 使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
  • 使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
  • 使用宏 va_end() 来清理赋予 va_list 变量的内存。

常用的宏有:

  • va_start(ap, last_arg):初始化可变参数列表。ap 是一个 va_list 类型的变量,last_arg 是最后一个固定参数的名称(也就是可变参数列表之前的参数)。该宏将 ap 指向可变参数列表中的第一个参数。

  • va_arg(ap, type):获取可变参数列表中的下一个参数。ap 是一个 va_list 类型的变量,type 是下一个参数的类型。该宏返回类型为 type 的值,并将 ap 指向下一个参数。

  • va_end(ap):结束可变参数列表的访问。ap 是一个 va_list 类型的变量。该宏将 ap 置为 NULL

现在让我们按照上面的步骤,来编写一个带有可变数量参数的函数,并返回它们的平均值:

复制代码
#include <stdio.h>
#include <stdarg.h>
 
double average(int num,...)
{
 
    va_list valist;
    double sum = 0.0;
    int i;
 
    /* 为 num 个参数初始化 valist */
    va_start(valist, num);
 
    /* 访问所有赋给 valist 的参数 */
    for (i = 0; i < num; i++)
    {
       sum += va_arg(valist, int);
    }
    /* 清理为 valist 保留的内存 */
    va_end(valist);
 
    return sum/num;
}
 
int main()
{
   printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
   printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}
复制代码

在上面的例子中,average() 函数接受一个整数 num 和任意数量的整数参数。函数内部使用 va_list 类型的变量 va_list 来访问可变参数列表。在循环中,每次使用 va_arg() 宏获取下一个整数参数,并输出。最后,在函数结束时使用 va_end() 宏结束可变参数列表的访问。

当上面的代码被编译和执行时,它会产生下列结果。应该指出的是,函数 average() 被调用两次,每次第一个参数都是表示被传的可变参数的总数。省略号被用来传递可变数量的参数。

Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

二、宏 __VA_ARGS__

从C99开始,宏可以接受可变数目的参数,就像可变参数函数一样。和函数一样,宏也用三个点 … 来表示可变参数。

  • 声明语法类似于可变参数函数:逗号后面三个句点"...",表示一个或多个参数。
  • 但常见编译器也允许传递0个参数。
  • 宏扩展时使用特殊标识符 __VA_ARGS__ 表示所传递的参数的替换。
  • 没办法访问可变参数列表内的单个参数,也不能获知多少个参数被传递。

__VA_ARGS__  宏用来表示可变参数的内容,简单来说就是将左边宏中的内容原样替换在右边 __VA_ARGS__ 所在的位置。

复制代码
#include <stdio.h>

#define debug(...)    printf(__VA_ARGS__)

int main(void)
{
    int year = 2023;
    debug("this year is %d\n", year);    // 效果等同于 printf("this year is %d\n", year);
}
复制代码

可变参数名字

通过一些语法,你可以给可变参数起一个名字,而不是使用 __VA_ARGS__  ,如下方代码中的 args

复制代码
#include <stdio.h>
#define debug(format, args...)  printf(format, args)
int main(void) {
    int year = 2023;
    debug("this year is %d\n", year);    // 效果等同于 printf("this year is %d\n", year);
}
复制代码

无参传入情况

与可变参数函数不同的是,可变参数宏中的可变参数必须至少有一个参数传入,否则,编译会报错(原因是多余一个逗号)。为了解决这个问题,需要一个特殊的 “##” 操作,如果可变参数被忽略或为空,“##” 操作将使预处理器(preprocessor)去除掉它前面的那个逗号。

复制代码
#include <stdio.h>

#define debug(format, args...)    printf(format, ##args)

int main(void)
{
    int year = 2023;
    debug("hello, world");    // 只有format参数,没有args可变参数
}
复制代码

 

posted on   NJ_Leon  阅读(51)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析

导航

点击右上角即可分享
微信分享提示