C/C++中不定参数的使用详解

 更新时间:2023年12月01日 09:55:06   作者:超人不会飞)  
这篇文章主要为大家详细介绍了C/C++中不定参数的使用的相关知识,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下

C语言的不定参

C语言的不定参数最常见的应用示例就是printf函数,如下,参数列表中的...表示不定参数列表

#include <stdio.h>
int printf(const char *format, ...);

试着模拟实现C语言的printf函数

void myprintf(const char *fmt, ...)
{
    //TODO
}

C语言中,对于...不定参列表,要用va_*系列宏函数操作

#include <stdarg.h>

​​​​​​​void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);

va_list

va_list可以理解为一个指针类型,开始时,通过调用va_start,它会指向不定参列表的第一个参数

va_start

我们知道,C/C++的函数参数通过压栈的方式传入,不定参列表的多个参数也需要压栈。

va_start的作用是使ap指向不定参列表的第一个参数。通过不定参数列表前的最后一个函数参数,找到不定参列表的首元素,并使ap(va_list类型的指针)指向首元素。因此,C函数使用...不定参数,前面至少包含一个其它参数,由它提供不定参数的起始位置。

va_arg

va_arg的作用是在不定参数列表中,从ap指向的参数开始,逐个返回type类型的数据,一般需要循环调用va_arg,在此过程中ap会不断往后走

va_end

ap使用结束后就失效了,为了避免野指针的引用,va_end可以销毁ap指针

va_copy

拷贝src到dest,使得二者指向同一个不定参数列表,src不用再次调用va_start,但要调用va_end

Test Demo:

void myprintf(size_t num, ...)
{
    va_list ap1;
    va_start(ap1, num);
    va_list ap2;
    va_copy(ap2, ap1);
    for (int i = 0; i < num; i++)
    {
        char ch = va_arg(ap1, int);
        std::cout << ch;
    }
    std::cout << std::endl;
    for (int i = 0; i < num; i++)
    {
        char ch = va_arg(ap2, int);
        std::cout << ch;
    }
    std::cout << std::endl;
    va_end(ap1);
    va_end(ap2);
}

int main()
{
    myprintf(3, 'L', 'O', 'L');
    return 0;
}

输出结果

[ckf@VM-8-12-centos 1.before]$ ./vargs 
LOL
LOL

注意:va_arg对于可变参数的处理是有一些规则的,特别是对于小整数类型(如char和float)。这些类型在传递给va_arg时会被默认转换为int和double,而不是它们的实际类型。因此,在Test Demo中,对于传入的char类型参数,在调用va_arg时也应该指定type为int,获得返回值后再转换为实际类型char。

以下函数可以对不定参数列表进行格式化操作。

#include <stdarg.h>

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

它们的作用与printf相同,都可以通过参数format,对不定参数进行格式化。区别在于:printf使用不定参...作为格式化的数据,而上面以v开头printf结尾的函数,使用ap指向的不定参数列表作为格式化的数据。除此之外,内部的区别就在于输出的方向了,vprintf将格式化后的字符串输出到stdout,vfprintf输出到指定的文件流,vsprintf和vsprintf输出到指定的内存空间str,且后者可以指定输出的字节个数。

Test Demo:

void myprintf(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);

    char str[64] = {0};
    int len = vsnprintf(str, sizeof(str), fmt, ap);
    str[len] = 0;
    std::cout << str << std::endl;

    va_end(ap);
}
int main()
{
    myprintf("%s %c %d", "你好", '!', 2024);
    return 0;
}

输出结果

[ckf@VM-8-12-centos 1.before]$ ./vargs 
你好 ! 2024

C++的不定参

补充:

  • args参数包作为参数传递给另一个函数时,记得加...,表示这个args参数是不定参数包
  • 函数接收Args...类型的参数包时,通常可以将参数设置为&&万能引用,减少拷贝的成本
  • C++11标准库中,很多构造方法都使用了以不定参数包为参数的条目,方便一些无法缺点参数个数的实例的构造

例如:

std::make_shared,由于并不确定构造智能指针指向的对象类型需要传入多少参数,C++11使用了Args不定参数包

emplace函数,C++11很多容器都重载了这个函数,emplace支持可变参数包,并且用参数包中的参数作为新插入元素构造函数的参数。

Test Demo

template <class T>
void myprintfcpp(T val) // 终止函数
{
    std::cout << val;
}

template <class T, class... Args>
void myprintfcpp(T val, Args &&...args)
{
    std::cout << val;
    myprintfcpp(std::forward<Args>(args)...);
}

int main()
{
    myprintfcpp(3, 'L', 'O', 'L');
    std::cout << std::endl;
    return 0;
}

输出结果

[ckf@VM-8-12-centos 1.before]$ ./vargs 
3LOL

到此这篇关于C/C++中不定参数的使用详解的文章就介绍到这了,更多相关C++不定参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言中常量指针与指针常量区别浅析

    C语言中常量指针与指针常量区别浅析

    这篇文章主要介绍了C语言中常量指针与指针常量区别,有需要的朋友可以参考一下
    2013-12-12
  • C++实现链表版本通讯录

    C++实现链表版本通讯录

    这篇文章主要为大家详细介绍了C++实现链表版本通讯录,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • 详解C++编程的多态性概念

    详解C++编程的多态性概念

    这篇文章主要介绍了C++编程的多态性概念,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • 理解数据结构

    理解数据结构

    本文是对数据结构对编程的重要性,数据结构对数据存储,数据处理,内存工作,从宏观上理解数据结构
    2016-07-07
  • C++中fork函数的使用及原理

    C++中fork函数的使用及原理

    这篇文章主要介绍了C++中fork函数的使用及原理,在C++中,fork函数用于创建一个新的进程称为子进程,该进程与原始进程几乎完全相同,需要的朋友可以参考下
    2023-05-05
  • c语言strftime时间格式化示例

    c语言strftime时间格式化示例

    C/C++程序中需要程序显示当前时间,可以使用标准函数strftime,本文提供一个示例供大家参考
    2014-02-02
  • Matlab绘制有趣的罗盘时钟的示例代码

    Matlab绘制有趣的罗盘时钟的示例代码

    这篇文章主要介绍了如何利用Matlab实现绘制有趣的罗盘时钟,文中的示例代码讲解详细,对我们学习Matlab有一定的帮助,需要的可以参考一下
    2023-01-01
  • 利用C语言实现将格式化数据和字符串相互转换

    利用C语言实现将格式化数据和字符串相互转换

    这篇文章主要为大家详细介绍了2个函数,分别是sprintf和sscanf,可以用来实现将格式化数据和字符串相互转换,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-03-03
  • socket多人聊天程序C语言版(二)

    socket多人聊天程序C语言版(二)

    这篇文章主要为大家详细介绍了socket多人聊天程序C语言版第二篇,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • VScode搭建OpenCV环境的详细步骤

    VScode搭建OpenCV环境的详细步骤

    用vscode来写opencv代码需要自己编译OpenCV,主要用到MinGW-w64和CMake工具。接下来通过本文给大家介绍VScode搭建OpenCV环境的相关知识,需要的朋友可以参考下
    2021-11-11

最新评论