C/C++——可变参数

C语言可变参数

接口概览

cpp 复制代码
#include <stdarg.h>

va_list ap;          // 参数指针类型
va_start(ap, last);  // 初始化:last必须是最后一个固定参数
va_arg(ap, type);    // 获取下一个参数,type指定类型
va_end(ap);          // 清理
va_copy(dest, src);  // 复制参数指针(C99),一般来说va_arg调用一次指针就会后移指向下一个参数,                                 /这个函数可以记录下之前某个参数的位置方便回退

使用示例

cpp 复制代码
#include<stdarg.h>

//使用参数来标记有多少个可变参数
string func(int count,...) 
{
    va_list list;
    va_list list2;
    va_start(list,count); 
    va_start(list2, count);

    int sum = 0;
    for (int i = 0;i<count;i++) 
    {
	    if (i == 1) 
	    {
	        va_copy(list2,list);
	    }
	    sum += va_arg(list,int);
    }

    int sum2 = 0;
    for (int i = 0;i<count-1; i++)
    {
	    sum2 += va_arg(list2, int);
    }

    va_end(list);
    va_end(list2);

    return string(to_string(sum)+":"+ to_string(sum2));
}

//使用尾标记来标记有多少个可变参数
int func2(int a,...)
{
    va_list list;
    va_start(list,a);

    int sum = 0;
    while(1)
    {
	    char* temp = va_arg(list,char*);
	    if (temp == nullptr)break;

	    sum += stoi(temp);
    }

    va_end(list);
    return sum;
}

int main() 
{
    string sum = func(4,1,2,3,4);
    int sum2 = func2(0,"1","2","3",nullptr);

    cout << sum << endl;
    cout << sum2 << endl;
}

原理概览

了解过函数栈帧的同学应该都知道,函数调用之后会把其收到的参数存放在函数栈中(一般是连续的),因此这个C语言可变参数出乎意料的简单:

特别注意

使用C语言可变参数的时候,要特别注意,va_start的调用之前最好不要调用其他函数,因为函数调用可能会导致可变参数在栈上的位置被优化等等,使得va_start无法找到最后一个固定参数的位置


C++可变参数

initializer_list(C++11)

cpp 复制代码
void func(initializer_list<int> list)
{
    for(auto it = list.begin();it!=list.end();++it) 
    {
	cout << (*it) << " ";
    }
    cout << endl;
}

int main()
{
    func({1, 5, 6, 3, 9});
}

原理:其实就是编译器把{1,5,6,3,9}转换成一个只读数组,然后把这个数组的指针等等信息存放到initializer_list对象中。

可变参数模版

递归展开参数包:

cpp 复制代码
void get_args(vector<int>& arr)
{
    return;
}

template<typename T,typename... Args>
void get_args(vector<int>& arr,T t,Args... args) 
{
    arr.push_back(t);
    get_args(arr,args...);
}

template<typename... Args>
void func(Args... args)//定义了一个可变参数包
{
    vector<int> arr;
    get_args(arr,args...);//展开可变参数

    cout << sizeof...(args) << endl;//sizeof...是专门获取可变参数包参数个数的操作符
    for (const auto& e : arr)
    {
	    cout << e << " ";
    }
    cout << endl;
}

int main() 
{
    func(5, 6, 3, 2, 7);
}

直接展开参数包:

cpp 复制代码
template<typename... Args>
vector<int> fun(Args... args)
{
    int arr[] = {args...};
    for (const auto& e : arr) { cout << e << " "; }
    cout << endl;
    return {args...};
}

int main() 
{
    vector<int>ret = fun(5, 6, 3, 2, 7);
    for (const auto& e : ret) { cout << e << " "; }
    cout << endl;
}

折叠表达式:

cpp 复制代码
1. 一元左折叠 (... op pack)

template<typename... Args>
auto sum(Args... args) {
    return (... + args);  // ((args1 + args2) + args3) + ...
}

2. 一元右折叠 (pack op ...)

template<typename... Args>
auto sum(Args... args) {
    return (args + ...);  // args1 + (args2 + (args3 + ...))
}

3. 二元左折叠 (init op ... op pack)

template<typename... Args>
auto sum_with_init(Args... args) {
    return (0 + ... + args);  // (((0 + args1) + args2) + args3)
}

4. 二元右折叠 (pack op ... op init)

template<typename... Args>
auto sum_with_init(Args... args) {
    return (args + ... + 0);  // args1 + (args2 + (args3 + 0))
}

原理

C++可变参数模版的的原理很简单:递归展开方式的原理就是递归模版实例化,而折叠表达式的原理就是编译时期直接展开。

相关推荐
BioRunYiXue1 小时前
科研干货 | IC50全面解读:概念解析、实验设计与数据分析要点
java·开发语言·javascript·人工智能·算法·数据挖掘·数据分析
边境悍匪1 小时前
蜗牛学苑 Java 智能体学习 Day34|AOP 进阶、权限思维导图复盘
java·开发语言·spring boot·学习
hele_two1 小时前
C++模拟蚁群(二)
c++·算法·数学建模·图形渲染
WK100%1 小时前
LeetCode双题:滑动窗口破解最小子数组
c++·经验分享·笔记·算法
每天吃饭的羊1 小时前
WebAssembly
开发语言·javascript·wasm
@福者1 小时前
C++ QT 入门
开发语言·c++·qt
沧海一笑-dj1 小时前
【C++】Visual Studio 2026下载和安装
c++·visual studio·vs·vs2026
长友cy2 小时前
Qt官方示例BluetoothLowEnergyGame源码阅读
开发语言·qt
是隼人2 小时前
buuctf-pwn PWN1题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门