各种输出函数的比较(printf/fprintf/sprintf/snprintf/vprintf/vfprintf/vsprintf/vsnprintf)
来源:互联网 发布:问道手游刷道辅助软件 编辑:程序博客网 时间:2024/05/29 13:20
对于程序猿来说,printf函数可以说是最熟悉的一个工具了。利用它可以将各类调试信息输出到指定的设备(比如串口)中,实现对程序运行状态的掌控和分析。不过,在实际的应用中,相信大家除了printf函数之外,应该还见过几个与其类似的函数,包括fprintf、sprintf、snprintf、vprintf、vfprintf、vsprintf、vsnprintf等等。那么,这些看上去很类似的函数之间,到底有什么区别,各自的作用到底是什么?今天就来总结一下。
首先列出全部的函数申明,以供参考。
#include <stdio.h>int printf(const char *format, ...);int fprintf(FILE *stream, const char *format, ...);int sprintf(char *str, const char *format, ...);int snprintf(char *str, size_t size, const char *format, ...);#include <stdarg.h>int vprintf(const char *format, va_list ap);int vfprintf(FILE *stream, const char *format, va_list ap);int vsprintf(char *str, const char *format, va_list ap);int vsnprintf(char *str, size_t size, const char *format, va_list ap);
怎么样,是不是看的有点晕?没关系,我们可以先用一个表格来大致区分一下上述这些函数的异同点,就不会那么晕了。
OK,下面我们就来逐个进行详细的介绍和比对。
一、printf和vprintf
多数情况下使用printf() 。只有当你需要自己写一个printf()那样的专有函数的时候才需要vprintf()。比如你写一个自己专门的错误输出函数:
int error(char *fmt, ...){ int result; va_list args; va_start(args, fmt); // 一些内容 va_end(args); return result;}
应当注意到,你不能转发参数给printf,因为printf是变长参数的,而不是vprintf的单独一个va_list。然而vprintf() 函数, 只取一个合并的va_list 参数, 所以完整的版本是:
int error(char *fmt, ...){ int result; va_list args; va_start(args, fmt); fputs("Error: ", stderr); result = vfprintf(stderr, fmt, args); va_end(args); return result;}
二、fprintf和vfprintf
两个函数从声明看,第三个参数有区别,这样就形成了两个函数不同的作用。比如,你要写一个日志函数:
void log(FILE *file, const char* format, ... ){ va_list args; va_start (args, format); fprintf(file, "%s: ", getTimestamp()); vfprintf (file, format, args); //在这个地方用vfprintf函数就很合适,因为第三个参数可以直接得到 va_end (args);}
vfprintf适合参数可变列表传递。
三、sprintf和vsprintf
先看一个例子:
#include <stdio.h>#include <stdafx.h>int _tmain(int argc, _TCHAR* argv[]){ char *p1="China"; char a[20]; sprintf(a,"%s",p1); printf("%s\n",a); memset(a,0,sizeof(a)); snprintf(a,3,"%s",p1); printf("%s\n",a); printf("%d\n",strlen(a)); return 0;}
结果输出:
China
Chi
3
过程分析:
sprintf(a,”%s”,p1) 把p1字符串拷贝到数组a中(‘\0’也拷贝过去了)。
snprintf(a,3,”%s”,p1) 拷贝P1中前3个字符到数组a中,并在末尾自动添加’\0’。
sprintf属于I/O库函数,snprintf函数并不是标准c/c++中规定的函数,但是在许多编译器中,厂商提供了其实现的版本。在gcc中,该函数名称就snprintf,而在VC中称为_snprintf。 如果你在VC中使用snprintf(),会提示此函数未声明,改成_snprintf()即可。
注意点:
1 sprintf是一个不安全函数,src串的长度应该小于dest缓冲区的大小,(如果src串的长度大于或等于dest缓冲区的大小,将会出现内存溢出。)
2 snprintf中源串长度应该小于目标dest缓冲区的大小,且size等于目标dest缓冲区的大小。(如果源串长度大于或等于目标dest缓冲区的大小,且size等于目标dest缓冲区的大小,则只会拷贝目标dest缓冲区的大小减1个字符,后加’\0’;该情况下,如果size大于目标dest缓冲区的大小则溢出。)
3 snprintf ()函数返回值问题, 如果输出因为size的限制而被截断,返回值将是“如果有足够空间存储,所应能输出的字符数(不包括字符串结尾的’\0’)”,这个值和size相等或者比size大!也就是说,如果可以写入的字符串是”0123456789ABCDEF”共16位,但是size限制了是10,这样 snprintf() 的返回值将会是16 而不是10!
四、snprintf和vsnprintf
同样来看一个例子:
#include <iostream> #include <cstring> #define snprintf _snprintf using namespace std; int main() { char str[10] = {0}; char *data = "abcdefg"; sprintf(str, "debug : %s", data); cout << str << endl; return 0; }
该程序可以编译过,但是在运行期间会崩溃,原因相信大家都能看的出来。那么,应该如何处理呢?
#include <iostream> #include <cstring> #define snprintf _snprintf using namespace std; int main() { char str[10] = {0}; char *data = "abcdefg"; snprintf(str, sizeof(str) - 1, "debug : %s", data); cout << str << endl; return 0; }
这样就安全了,和strncpy非常类似。
另外,需要特别注意的是: Windows和Linux中的snprintf函数有区别, 在linux代码中,经常见到snprintf(str, sizeof(str), “…”)这样的用法, 为什么这里不是sizof(str) - 1呢?
我们看看Windows下这么用会怎样:
#include <iostream> #include <cstring> #define snprintf _snprintf using namespace std; int main() { char str[10] = {0}; char *data = "abcdefgddddddddddddddddddddd"; snprintf(str, sizeof(str), "debug : %s", data); cout << str << endl; return 0; }
我运行的时候,程序没有崩溃,算是万幸。 但结果乱码。看来,没有自动在str最后加’\0’, 在linux中, 就安全了, 会自动补哈, 所以永远不会越界。
总结一下:
1. Linux中, 对于snprintf, 用sizeof(str), 最后会自动加’\0’, 比strncpy更安全省事。
2. Windows中, 就把snprintf和strncpy理解为类似的, 要用sizeof(str) - 1, 需要注意最后的’\0’, 当然啦,你可以在每次用strncpy之前,利用memset将串清零, 这样比较好。VC++6.0中的_snprintf(snprintf)并没有按要求实现, 晕。
- 各种输出函数的比较(printf/fprintf/sprintf/snprintf/vprintf/vfprintf/vsprintf/vsnprintf)
- printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 输出格式转换函数
- printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 输出格式转换
- printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 输出格式转换
- printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 输出格式转换
- printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - 输出格式转换
- 格式符函数printf()、sprintf()、 vprintf()、 vsprintf()、 fprintf() 和 vfprintf()
- PHP字符串函数之 sscanf echo print sprintf vsprintf printf vprintf fprintf vfprintf
- printf、fprintf、dprintf、sprintf、snprintf、vprintf系列
- printf/fprintf/sprintf/snprintf输出函数
- sprintf,snprintf,vsprintf,vsnprintf
- PHP格式化输出printf,sprintf,vprintf,fprintf
- vprintf, vfprintf和 vsprintf 函数
- vfprintf、vprintf和vsprintf函数
- vfprintf、vprintf和vsprintf函数
- va_list,va_start,va_end,va_copy,vprintf,vfprintf,vsprintf,vsnprintf
- keil C51:printf sprintf vprintf vsprintf 函数学习
- printf、fprintf、sprintf和snprintf函数
- Ubuntu14.04下使用cmake实现多版本opencv管理
- JDK自带工具keytool生成ssl证书(转)
- 数据框取数柱形图(仅做示例,所用数据不符合逻辑)
- SDN环境的配置(Mininet+Floodlight1.2控制器的安装)
- POWERDESIGNER 常用设置
- 各种输出函数的比较(printf/fprintf/sprintf/snprintf/vprintf/vfprintf/vsprintf/vsnprintf)
- 奥威旅游业数据分析软件Power-BI 之国内住宿设施分析
- jfianl中的图形验证
- Python 闭包问题
- 快速查找SO所对应的交货单DN及PO
- 阿里云服务器连接不上3306端口,解决
- 抽象类和接口的区别 转
- springMVC3+hibernate3升级为springMVC4+hibernate4步骤
- Java中有意思的n++