C/C++不定参函数

发布于:2024-04-20 ⋅ 阅读:(18) ⋅ 点赞:(0)

        在学习C语言的时候,往往使用printf进行打印,其中printf函数就是一个不定参的函数,在这个函数内部可以根据格式化字符串中格式化的字符,分别获取不同的参数进行数据的格式化。这里对这类不定参的使用简要做出小结。

C语言中不定参数

        C语言中需要引入stdarg.h的头文件,使用其中的va_list、va_start、va_arg和va_end。

1.va_list:是一个类型,用于声明一个变量,用该变量来存储不定参数的信息。

2.va_start:用于初始化va_list,让va_list指向不定参数的起始位置,可以接受两个参数,第一个是va_list对象,第二个是用于确定不定参数的起始位置。

3.va_arg:用于获取当前位置的值,在每一次使用以后,会将指针移动到下一个可变参数的位置,可以接受两个参数,一个是va_list,一个是要获取的参数的类型。

4.va_end:用于清理va_list对象,确保在使用完不定参以后正确的释放资源。

补充:vasprintf:动态分配内存来存储格式化之后的字符串,但可以接受可变参数,int vasprintf (char **buf, const char *format, va_list ap),buf分别表示指向char指针的指针,用来存储格式化后的字符串地址。format是一个格式化字符串,包含要打印的文本和格式说明符,ap表示可变参数列表。

vasprintf会根据format字符串和可变参数列表ap的内容动态的分配足够的内存来存储格式化后的字符串,并将地址存储在buf指针中,如果成功,就会返回格式化后的字符串的长度。

#include <iostream>
#include <cstdarg>
void printNum(int n, ...)
{
    va_list al;     //定义一个变量,后面用来存储不定参数的信息
    va_start(al, n); // 初始化va_list,让va_list指向不定参数列表的起始位置(让al和不定参产生联系)
    for (int i = 0; i < n; i++)
    {
        int num = va_arg(al, int); // 此时al与不定参就产生了绑定,使用va_arg来取出当前位置的参数,取完以后会自动往后移一步
        std::cout << num << std::endl;
    }
    va_end(al); // 清空可变参数列表--其实是将al置空
}
int main()
{
    printNum(3, 11, 22, 33);
    printNum(5, 44, 55, 66, 77, 88);
    return 0;
}
#include <iostream>
#include <cstdarg>
void myprintf(const char *fmt, ...)
{
    // int vasprintf(char **strp, const char *fmt, va_list ap);
    char *res;
    va_list al;     //1.初始化
    va_start(al, fmt);  //2.绑定,设置起始位置
    int len = vasprintf(&res, fmt, al); //3.按照fmt格式来打印al中的内容,fmt是用户传入的
    va_end(al);    //4.结束
    std::cout << res << std::endl;
    free(res);
}
int main()
{
    myprintf("%s-%d", "⼩明", 18);
    return 0;
}

        可以这样来理解:va_list只是定义了一个变量来存储,是个空壳。当使用va_start以后,会让这个空壳与不定参产生联系,也就可以理解成将不定参的变量都存储在这个空壳里了,此时也就不是空壳了。此时我只需要不断地使用va_arg去从va_list中取出数据即可。使用完毕以后就用va_end关闭。

不定参宏函数

不定参宏函数经常使用__VA_ARGS__来表示不定参

#include <iostream>
#include <cstdarg>
#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
int main()
{
    LOG("%s-%s", "hello", "你好");
    return 0;
}

        可以理解为:定义了一个LOG函数,该函数有不定参,调用该函数相当于调用printf,__FILE__表示文件名,__LINE__表示行号,这两个都是stdio.h头文件中的宏,fmt表示参数fmt,##__VA_ARGS__相当于不定参数...,##是为了处理不带参的情况,可以替换以后再理解。

C++不定参函数

使用基本模版

template<typename ...Args>
void print(Args... args) {}

1.递归获取

#include <iostream>

void print() {
    //递归到最后,参数都消耗光了,需要一个无参的函数来兜底
}

template <typename T, typename... Args>
void print(T first, Args... args) {
    std::cout << "Cur : " << first << " args size : " << sizeof...(args) << std::endl;
    print(args...);  // 递归使用,每次都可以把第一个参数给消耗掉
}

int main() {
    print("nihao", "hello", 11, 222);
    return 0;
}

       可以这样来理解:定义了一个模版函数,第一个是类型为T的参数,第二个是不定参数,长度未知。每一次递归调用的时候,不定参中的第一个参数就会传入到first这个位置,这样就相当于不定参的一个参数被消耗掉了。接着不断递归,不定参每递归一次都会少一个参数,直到最后没有参数了。

#include <iostream>
#include <cstdarg>
#include <memory>
#include <functional>
void xprintf()
{
    std::cout << std::endl;
}
template <typename T, typename... Args>
void xprintf(const T &value, Args &&...args)
{
    std::cout << value << " ";
    if ((sizeof...(args)) > 0)  //可以使用sizeof ...(args)的方式来计算不定参的长度
    {
        xprintf(std::forward<Args>(args)...);   //将不定参递归传入,不定参的第一个参数就会顶替value位置
    }
    else
    {
        xprintf();
    }
}
int main()
{
    xprintf("hello");
    xprintf("hello", 666);
    xprintf("nihao", "hello", 666);
    return 0;
}