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 = ;
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 != && sex != ) {
inputError("main", "性别是1或2!(输入值: %d)\n", sex);
return EXIT_FAILURE;
} /* 输入年龄 */
puts("请输入年龄:");
scanf("%d", &age);
if ( age < || age > ) {
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",,);
return ;
}

执行的结果为:

21

果然验证了我的想法,看来这一类函数的用法就是利用va_list记录可变参数位置的特性,将母函数的可变参数列表通过va_list变量转化给v...printf函数的,也就是说跟...printf类的函数是一样的,只不过把...printf类函数的可变参数列表省略,替换成从va_list变量调用而已!

C语言中 v...printf类函数的用法的更多相关文章

  1. C++语言中std::array的神奇用法总结,你需要知道!

    摘要:在这篇文章里,将从各个角度介绍下std::array的用法,希望能带来一些启发. td::array是在C++11标准中增加的STL容器,它的设计目的是提供与原生数组类似的功能与性能.也正因此, ...

  2. C++中cout输出字符串和字符串型指针地址值的方法以及C语言中的printf用法比较

    #include <iostream> using namespace std; #include <stdio.h> int main() { char *pstr = &q ...

  3. c语言中external,static关键字用法

    static用法: 在C中,static主要定义全局静态变量.定义局部静态变量.定义静态函数. 1.定义全局静态变量:在全局变量前面加上关键字static,该全局变量变成了全局静态变量.全局静态变量有 ...

  4. C语言中的printf函数的输入输出问题

    这个问题是个很基础,但是我一直不知道的问题,是同学问了之后才知道的,长知识了. 这里要注意的是,printf函数是从右向左计算,从左向右输出. 距离如下: #include <stdio.h&g ...

  5. C语言中mktime函数功能及用法

    今天联系写一个日历的程序,需要算出月份中的第一天是星期几,用到了mktime()这个函数,感觉这个函数挺有用的,分享给大家. 原型:time_t mktime(struct tm *) 其中的tm结构 ...

  6. [原创]c语言中const与指针的用法

    最近一直在准备笔试,补补大一大二欠下的课.复习c语言时碰见这么个题:   1 2 3 4 5 int a=248, b=4; int const c=21; const int *d=&a;  ...

  7. C语言中printf与i++,C++中的cout

    一,printf与i++ 1,C语言中的printf是自右向左输出,. 2,而i++与++i不同的 i++首先取得i的值,下一行时候i = i + 1: ++i,首先i = i + 1,再取得i的值. ...

  8. C语言中malloc()和calloc()c函数用法

    C语言中malloc()和calloc()c函数用法   函数malloc()和calloc()都可以用来动态分配内存空间,但两者稍有区别. malloc()函数有一个参数,即要分配的内存空间的大小: ...

  9. 在不同语言中static的用法

    static (计算机高级语言) 编辑 像在VB,C#,C,C++,Java,PHP中我们可以看到static作为关键字和函数出现,在其他的高级计算机语言如FORTRAN.ALGOL.COBOL.BA ...

随机推荐

  1. loadrunner使用system()函数调用Tesseract-OCR识别验证码遇到的问题

    俗话说前人栽树,后人乘凉,此话一点不假,结合云层的一遍文章:http://bbs.51testing.com/thread-533920-1-1.html,知道还有一个Tesseract-OCR可以用 ...

  2. java:打包

    包名命名规范: 1.包名全部小写 2.包名一般情况下是域名的倒过来写+个性命名,如:tinyphp.com,就写成com.tinyphp+.xxx 打包方法 package + 包名 package ...

  3. Spring AOP: Spring之面向方面编程

    Spring AOP: Spring之面向方面编程 面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP). 面向对象将应用程序分解成 各个层次的对象,而AOP将程序分解 ...

  4. Android Handler与多线程

    本文首先解释一下handler是用来干嘛的,然后通过例子介绍其在多线程中的应用. 什么是Handler handler通俗一点讲就是用来在各个进程之间发送数据的处理对象.在任何进程中,只要获得了另一个 ...

  5. UVa 1402 Runtime Error 伸展树

    Runtime Error 到现在连样例也跑不出来!!! 调试了一晚上快要死了…… 知道错在哪里但是不会改,代码先扔在这里吧.看来不能太依赖模板啊orz…… #include <cstdio&g ...

  6. 【原创】30分钟入门 github

    很久没更新了,这篇文章重点在github的入门使用,读者可以下载github for windows shell,边看边操作,加深印象. 好了,30分钟的愉快之旅开始吧: 一.github使用的注意事 ...

  7. sql 随笔 2015-06-30

    清除多余字符 --清除多余字符 --' --char(9) 水平制表符 --char(10)换行键 --char(13)回车键 REPLACE( REPLACE( REPLACE(REPLACE([P ...

  8. 在Ubuntu下安装imx6linux系统的交叉编译环境遇到的问题总结

    这段时间一直忙于手上的嵌入式项目,可以说自己从嵌入式的菜鸟一点点的入门了,关于嵌入式和imx6核心板的开发有了一点的了解,尤其是对于板子环境的搭建.硬件的开发,搭建环境,是一个很大的工程量,也是很重要 ...

  9. Eclipse中查看JDK源码设置

    设置方法如下: 1.路径 window-> Preferences -> Java -> Installed JRES 2.此时"Installed JRES"右 ...

  10. Java开发之单例设计模式

    设计模式之单例模式: 一.单例模式实现特点:①单例类在整个应用程序中只能有一个实例(通过私有无参构造器实现):②单例类必须自己创建这个实例并且可供其他对象访问(通过静态公开的访问权限修饰的getIns ...