C语言的自学渐渐接近尾声,今天学到了标准库中的stdarg.h头,里面关联了stdio.h头里面的一类函数:v...printf函数,里面举的例子看了之后还是不太明白,google了一下依旧不是很懂,所以自行测试了一下,以下是过程:
这一类函数书中列举了四个,原型分别有:
int vfprintf(FILE *restrict stream, const char * restrict format, va_list arg); int vprintf(const char * restrict format, va_list arg); int vsnprintf(char * restrict s, size_t n, const char * restrict format, va_list arg); int vsprintf(char * restrict s, const char * restrict format, va_list arg);
书中用vfprintf函数举例:
假设程序需要不时地显示出错消息,而且我们希望每条消息都以以下格式的前缀开始:
** Error n:
这里的n在显示第一条出错消息的时候是1,以后每显示一条错误消息增加1。为了使产生出错消息更加容易,我们将编写一个名为errorf的函数。此函数类似于printf函数,但是它总在输出的开始添加了** Error n:,并且总是向stderr而不是stdout输出。errorf函数将调用vfprintf函数来完成大部分实际输出工作。下面是errorf函数可能的写法:
int errorf(const char *format, ...) { static int num_errors = 0; int n; va_list ap; num_errors++; fprintf(stderr, "** Error %d: ", num_errors); va_start(ap, format); n = vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); return n; }
看完例子之后我依然不知道这个函数究竟有什么作用,于是我google了一下,发现了一个网站举了这样一个例子:
/* vfprintf example */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> /* functions */ void inputError(char *function, char *format, ...); /* main */ int main(void) { int sex, age; /* 输入性别 */ puts("请输入性別:(1:男性 2:女性)"); scanf("%d", &sex); if ( sex != 1 && sex != 2 ) { inputError("main", "性别是1或2!(输入值: %d)\n", sex); return EXIT_FAILURE; } /* 输入年龄 */ puts("请输入年龄:"); scanf("%d", &age); if ( age < 0 || age > 150 ) { inputError("main", "年龄范围[0,150]!(输入值: %d)\n", age); return EXIT_FAILURE; } return EXIT_SUCCESS; } /** * @brief 错误处理 * @param[IN] function 函数名 * @param[IN] format 格式化字符串 * @param[IN] ... 可变长参数 */ void inputError(char *function, char *format, ...) { va_list arg; va_start(arg, format); fprintf(stderr, "发生错误的函数: %s\n", function); /* 显示错误信息 */ vfprintf(stderr, format, arg); va_end(arg); }执行结果:
请输入性別:(1:男性 2:女性)
3
发生错误的函数: main
性别是1或2!(输入值: 3)
我看完例子之后有点模模糊糊的感觉了,难道说这类函数是将调用它们的函数的可变参数列表转化为自己的可变参数列表,然后通过格式匹配将这些参数输出到流中的?
于是我自己写了个小函数测试一下:
#include<stdio.h> #include<stdlib.h> #include<stdarg.h> int func(char *str, ...) { int n; va_list ap; va_start(ap, str); n = vfprintf(stdout,str,ap); va_end(ap); return n; } int main(void) { func("%d%d\n",2,1); return 0; }
执行的结果为:
21
果然验证了我的想法,看来这一类函数的用法就是利用va_list记录可变参数位置的特性,将母函数的可变参数列表通过va_list变量转化给v...printf函数的,也就是说跟...printf类的函数是一样的,只不过把...printf类函数的可变参数列表省略,替换成从va_list变量调用而已!
时间: 2024-11-03 05:35:44