在学习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;
}