C风格可变参数函数

环境:Ubuntu gcc11.3.0

可变参数是什么

可变参数是指函数可以接收不确定个数的参数,…表示可变参数包,它必须要写在正常参数表的后面,例如我们熟知的printf函数定义:

int printf(const char *format, ...);

printf的第一个参数为char* fmt,后面则是不定参数包。printf的用法都知道就不赘述。这里贴一个使用示例:printf("%d,%d,%d",1,2,3);

之所以会使用不定参数就是因为我们并不知道用户具体会传入几个参数,所以在函数体内部应该如何进行解包呢?

如何解包对参数进行使用

C语言中要对可变参数进行使用,需要使用其提供的几个宏:

在这里插入图片描述

其中,va_list 是一个在 C 语言中用于处理不定参数的类型。它定义在 <stdarg.h> 头文件中,并提供了一组宏函数来访问不定参数列表,其中最常用的是 va_startva_argva_end

下面是这些宏函数的详细介绍:

  1. va_listva_list 是一个用于保存可变参数信息的类型。你可以将其声明为一个变量,通常命名为 ap

  2. va_startva_start 宏函数用于初始化 va_list 变量。它接受两个参数,第一个参数是 va_list 变量名,第二个参数是最后一个命名参数。va_start 宏函数在初始化 va_list 变量后,使其指向可变参数列表中的第一个参数。

  3. va_argva_arg 宏函数用于访问可变参数列表中的每个参数。它接受两个参数,第一个参数是 va_list 变量名,第二个参数是参数的类型。va_arg 宏函数返回指定类型的参数,并将 va_list 指针指向下一个参数。

  4. va_endva_end 宏函数用于清理 va_list 变量。它接受一个参数,即 va_list 变量名。va_end 宏函数在使用完可变参数列表后调用,以确保清理资源。

下面是一个简单的示例,演示了如何使用 va_list 相关的宏函数来实现一个自定义的可变参数函数:

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

void print_integers(int num, ...)//函数中必须包含命名参数,否则va_start无法确认可变参数的起始位置
{
    va_list ap;
    va_start(ap, num);

    for (int i = 0; i < num; i++) {
        int value = va_arg(ap, int);
        printf("%d ", value);
    }

    va_end(ap);
}

int main()
{
    print_integers(3, 1, 2, 3); // 输出:1 2 3
    return 0;
}

在上面的示例中,print_integers 函数接受一个整数参数 num,后面跟着不定数量的整数参数。它使用 va_list 相关的宏函数来访问并打印这些整数参数。首先,它调用 va_start 来初始化 va_list 变量 ap,然后使用一个循环和 va_arg 宏函数来访问每个整数参数,并使用 printf 函数打印出来。最后,它调用 va_end 来清理 va_list 变量。

这就是C语言如何使用可变参数列表的方法。

其他情况

printf函数为例,虽然上方已经对可变参数包进行了解包并使用,我们可能传入各种类型的数据,此时像上方代码一样在va_arg()中第二个参数固定的填写一个int有点不切实际,因为类型代表着内存中的寻址大小,统一格式显然会致使解析可变参数列表出现问题,无法解析所有元素。

为了解决这个问题,使用C语言提供的函数vasprintf()
在这里插入图片描述

vasprintf()函数是C语言中的一个可变参数函数,用于将可变参数按照指定的格式化字符串进行格式化,并将结果存储在动态分配的字符串中(记得手动释放该字符串)。

参数解析:

  • strp:一个指向字符指针的指针,用于接收格式化后的字符串。该指针将被设置为指向动态分配的内存块,其中包含格式化后的字符串。

  • format:一个字符串,用作格式化的模板。它可以包含普通字符和格式化指示符。

  • ap:一个va_list类型的参数,包含了可变参数列表。

    需要注意的是,vasprintf()函数在C标准中并不是标准函数,但它在许多系统中都可用,并且非常有用。如果你的系统不支持vasprintf()函数,你可以尝试使用vsnprintf()函数来实现类似的功能。
    在这里插入图片描述

此处我黏贴一个自己实现的printf()函数代码展示可变参数列表的使用

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
void MyPrint(const char* fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    char *buffer;
    
    vasprintf(&buffer,fmt,ap);
    
    printf("%s",buffer);
    va_end(ap);
    free(buffer);//一定要释放,否则会内存泄漏,vasprintf把数据写进了堆空间。
}

int main(){
    MyPrint("%s - %d\n","李四",1); // 输出:李四 - 1
    return 0;
}

注意事项

  1. ​ 可变参数列表前可以有多个命名参数。可变参数列表的定义可以出现在固定参数之后,允许在函数声明或定义中同时存在命名参数和可变参数。
#include <stdio.h>
#include <stdarg.h>

///
//可变参数列表前有多个命名参数的情况
void print_info(const char* format, int count, ...) {
    va_list args;
    va_start(args, count);

    printf("Format: %s\n", format);
    printf("Count: %d\n", count);

    for (int i = 0; i < count; i++) {
        int num = va_arg(args, int);
        printf("Argument %d: %d\n", i+1, num);
    }

    va_end(args);
}
int main() {
    print_info("Printing integers:", 3, 10, 20, 30);
    return 0;
}
//在上面的示例中,`print_info()`函数接受三个参数:`format`、`count`和可变参数列表。
//`format`参数是一个字符串,用于指定打印格式。`count`参数表示后续可变参数的个数。
//在函数内部,我们使用`va_start()`函数初始化`va_list`变量,并通过`va_arg()`函数遍历可变参数列表。
  1. 可变参数列表前必须要有命名参数,否则无法确认可变参数列表的起始位置。
    同时,也无法确认可变参数列表的结束位置。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值