【C语言】—— 可变参数列表

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 【C语言】—— 可变参数列表

C语言中的可变参数是一种特殊的函数参数,允许在函数定义中传递可变数量的参数。使用可变参数机制可以使函数更加灵活,可以根据不同的需求接受不同数量或类型的参数。

 



(一)概念理解

C语言提供了 <stdarg.h> 头文件,其中包含一组宏来处理可变参数。以下是使用可变参数的详细步骤:

  • 定义函数原型:在函数原型中,使用省略号 ... 表示函数参数的可变部分
void functionName(int fixed_arg, ...);
  • 在函数定义中声明 va_list 类型的变量和一个标识符:
#include <stdarg.h>
void functionName(int fixed_arg, ...)
{
    va_list variable_arguments;  // 可变参数列表的变量
    type arg;                    // 参数标识符
}

当我们到编译器中去查看时,我们不难发现 va_list 就是一个 char类型的指针而已

 

  • 使用 va_start 宏开始访问可变参数列表:
#include <stdarg.h>
void functionName(int fixed_arg, ...)
{
    va_list variable_arguments;
    va_start(variable_arguments, fixed_arg);
}

【说明】

  • va_start 宏接受两个参数,第一个参数是 va_list 类型的变量;
  • 第二个参数是固定参数的最后一个参数名。

同样的,在编译器中我们可以去查看背后的定义:

  • 这个宏比较好理解,结合栈帧中临时参数的压入位置
  • 使用 va_arg 宏获取每个可变参数的值,并按照相应的类型进行处理:

 

#include <stdarg.h>
void functionName(int fixed_arg, ...)
{
    va_list variable_arguments;
    va_start(variable_arguments, fixed_arg);
    type arg = va_arg(variable_arguments, type);
}

【说明】  

  • va_arg 宏接受两个参数,第一个参数是 va_list 类型的变量;
  • 第二个参数是可变参数的类型。

我们也可以在编译器下进行查看定义的操作:

分析:

  1. 这个函数的设计十分的巧妙,先让ap指向下个元素,然后使用【相对位置-偏移量】,访问当前元素;
  2. 访问了当前元素的同时,还让ap 指向下个位置的元素
  • 使用 va_end 宏结束可变参数的访问:
#include <stdarg.h>
void functionName(int fixed_arg, ...)
{
    va_list variable_arguments;
    va_start(variable_arguments, fixed_arg);
    // 使用 va_arg 获取和处理参数值
    va_end(variable_arguments);
}

【说明】

  • va_end 宏接受一个参数,即 va_list 类型的变量名。

同样的,我们同样可以在编译器下查看对应的定义:

 

  • 这个就很好理解了,将ap指向为null

除了上述之外,最难以理解的可能就是以下这个宏函数了:

【说明】

 

使用上述步骤,可以在函数中访问并处理可变数量的参数。需要注意的是,可变参数的传递方式是靠堆栈的,因此调用函数时需确保提供的参数与函数定义中的参数一致,否则会导致未定义行为。


(二)代码展示

1、求和问题

下面是一个使用可变参数的简单示例代码:

#include <stdio.h>
#include <stdarg.h>
// 可变参数函数,计算可变参数的和
int sum(int count, ...)
{
    int total = 0;
    // 声明可变参数列表
    va_list args;
    va_start(args, count);
    // 遍历可变参数并求和
    for (int i = 0; i < count; i++)
    {
        int num = va_arg(args, int);  // 获取下一个可变参数的值
        total += num;
    }
    va_end(args);  // 结束可变参数的访问
    return total;
}
int main()
{
    int result1 = sum(3, 10, 20, 30);
    printf("Sum 1: %d\n", result1);
    int result2 = sum(5, 1, 2, 3, 4, 5);
    printf("Sum 2: %d\n", result2);
    return 0;
}

【说明】

  1. 在上述代码中,我们定义了一个可变参数函数 sum,它可以接受不定数量的整数参数并返回它们的总和;
  2. main 函数中,我们分别调用了 sum 函数两次。第一次传递了 3 个参数,分别是 10、20 和 30;
  3. 第二次传递了 5 个参数,分别是 1、2、3、4 和 5。每次调用后,我们将计算得到的总和打印出来。

运行上述代码,输出结果如下:

 

2、求max值

当涉及到求一组数字中的最大值时,可变参数非常适合使用。以下是一个使用可变参数求最大值的例子:

int find_max(int count, ...)
{
    int max_value = 0;
    va_list args;  // 定义可变参数列表
    va_start(args, count);  // 初始化可变参数列表
    for (int i = 0; i < count; i++)
    {
        int num = va_arg(args, int);  // 获取可变参数列表中的当前整数
        if (i == 0 || num > max_value)
        {
            max_value = num;
        }
    }
    va_end(args);  // 结束可变参数列表
    return max_value;
}
int main()
{
    printf("最大值为:%d\n", find_max(5, 3, 7, 2, 9, 5));  // 输出:9
    printf("最大值为:%d\n", find_max(3, 10, 30, 20));     // 输出:30
    printf("最大值为:%d\n", find_max(1, 100));            // 输出:100
    printf("最大值为:%d\n", find_max(0));                 // 输出:0
    return 0;
}

【说明】

  1. 在上述示例中,我们定义了一个 find_max 函数来求解最大值。函数中的 count 参数表示可变参数的数量,而 ... 表示可变参数的列表。
  2. 在函数中,我们使用 va_list 来定义可变参数列表,并使用 va_startva_arg 宏来遍历和访问每个可变参数。通过比较当前参数和最大值,我们逐步更新最大值。最后,我们使用 va_end 宏来结束可变参数列表。
  3. main 函数中,我们调用 find_max 函数,并传递不同数量的参数。然后,将返回的最大值打印到控制台上。

运行上述代码,输出结果如下:

此时,如果将参数类型改成 char 类型,求char类型变量中的最大值,上述求 max 的逻辑代码后还会有问题吗?

如果将参数类型改为 char 类型,上述的逻辑代码仍然可以工作,但最大值的比较可能会出现问题。这是因为 char 类型在 C 语言中被视为整数类型,而不是字符类型。

  • 代码如下:
int find_max(int count, ...)
{
    int max_value = 0;
    va_list args;  // 定义可变参数列表
    va_start(args, count);  // 初始化可变参数列表
    for (int i = 0; i < count; i++)
    {
        int num = va_arg(args, int);  // 获取可变参数列表中的当前整数
        if (i == 0 || num > max_value)
        {
            max_value = num;
        }
    }
    va_end(args);  // 结束可变参数列表
    return max_value;
}
int main()
{
    
    char a = '1'; //ascii值为:49
    char b = '2'; //ascii值为:50
    char c = '3'; //ascii值为:51
    char d = '4'; //ascii值为:52
    char e = '5'; //ascii值为:53
  
    printf("最大值为:%d\n", find_max(5,a,b,c,d,e));
    return 0;
}

运行上述代码,输出结果如下:

我们可以发现结果并未收到影响,但是我们解析的时候是按照 va_arg(arg,int) 来解析的?

  1. 因为即使将参数类型改为 char 类型,上述逻辑代码仍然可以工作,而且结果并未受到影响。这是因为在 C 语言中,char 类型在传递给可变参数函数时会被自动提升为 int 类型。
  2. 当使用 va_arg 宏来解析参数时,确实是按照 va_arg(arg, int) 的方式进行解析,即将参数解析为 int 类型。这是因为可变参数函数在执行时,并不知道参数的具体类型,所以需要通过提供的类型信息来正确解析参数。
  3. 因此,即使我们将参数类型改为 char,在使用 va_arg 解析参数时仍然使用 int 类型,但由于 char类型会被自动提升为 int 类型,所以代码仍然可以正常工作并得到正确的结果。
  4. 综上所述,尽管参数类型被定义为 char 类型,但在解析参数时使用 va_arg(arg, int) 是没有问题的,因为传递的实际参数会自动提升为 int 类型。

总结

以上便是关于C语言中可变参数的全部知识了。感谢大家的观看与支持!!!

 

相关文章
|
6月前
|
存储 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(下)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
88 5
|
6月前
|
算法 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(中)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
57 2
|
6月前
|
算法 编译器 C语言
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题(上)
从C语言到C++_34(C++11_下)可变参数+ lambda+function+bind+笔试题
38 1
|
6月前
|
编译器 C语言 C++
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(上)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
39 1
|
6月前
|
Java 编译器 C语言
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(下)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
29 0
|
6月前
|
C语言 C++
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(中)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
44 0
|
12月前
|
存储 安全 C语言
|
6月前
|
存储 编译器 Serverless
C语言可变参数
C语言可变参数
55 0
|
6月前
|
Serverless C语言
【C语言】va_list(可变参数处理)
【C语言】va_list(可变参数处理)
118 0
|
C语言
c语言数据结构-循环列表
c语言数据结构-循环列表