各种输出函数的比较(printf/fprintf/sprintf/snprintf/vprintf/vfprintf/vsprintf/vsnprintf)
對于程序猿來說,printf函數(shù)可以說是最熟悉的一個(gè)工具了。利用它可以將各類調(diào)試信息輸出到指定的設(shè)備(比如串口)中,實(shí)現(xiàn)對程序運(yùn)行狀態(tài)的掌控和分析。不過,在實(shí)際的應(yīng)用中,相信大家除了printf函數(shù)之外,應(yīng)該還見過幾個(gè)與其類似的函數(shù),包括fprintf、sprintf、snprintf、vprintf、vfprintf、vsprintf、vsnprintf等等。那么,這些看上去很類似的函數(shù)之間,到底有什么區(qū)別,各自的作用到底是什么?今天就來總結(jié)一下。
首先列出全部的函數(shù)申明,以供參考。
#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);怎么樣,是不是看的有點(diǎn)暈?沒關(guān)系,我們可以先用一個(gè)表格來大致區(qū)分一下上述這些函數(shù)的異同點(diǎn),就不會(huì)那么暈了。
| 可變參數(shù) | printf | fprintf | sprintf、snprintf |
| 固定參數(shù) | vprintf | vfprintf | vsprintf、vsnprintf |
OK,下面我們就來逐個(gè)進(jìn)行詳細(xì)的介紹和比對。
一、printf和vprintf
多數(shù)情況下使用printf() 。只有當(dāng)你需要自己寫一個(gè)printf()那樣的專有函數(shù)的時(shí)候才需要vprintf()。比如你寫一個(gè)自己專門的錯(cuò)誤輸出函數(shù):
int error(char *fmt, ...) {int result;va_list args;va_start(args, fmt);// 一些內(nèi)容va_end(args);return result; }應(yīng)當(dāng)注意到,你不能轉(zhuǎn)發(fā)參數(shù)給printf,因?yàn)閜rintf是變長參數(shù)的,而不是vprintf的單獨(dú)一個(gè)va_list。然而vprintf() 函數(shù), 只取一個(gè)合并的va_list 參數(shù), 所以完整的版本是:
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
兩個(gè)函數(shù)從聲明看,第三個(gè)參數(shù)有區(qū)別,這樣就形成了兩個(gè)函數(shù)不同的作用。比如,你要寫一個(gè)日志函數(shù):
void log(FILE *file, const char* format, ... ) {va_list args;va_start (args, format);fprintf(file, "%s: ", getTimestamp());vfprintf (file, format, args); //在這個(gè)地方用vfprintf函數(shù)就很合適,因?yàn)榈谌齻€(gè)參數(shù)可以直接得到va_end (args); }vfprintf適合參數(shù)可變列表傳遞。
三、sprintf和vsprintf
先看一個(gè)例子:
#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; }結(jié)果輸出:
China
Chi
3
過程分析:
sprintf(a,”%s”,p1) 把p1字符串拷貝到數(shù)組a中(‘\0’也拷貝過去了)。
snprintf(a,3,”%s”,p1) 拷貝P1中前3個(gè)字符到數(shù)組a中,并在末尾自動(dòng)添加’\0’。
sprintf屬于I/O庫函數(shù),snprintf函數(shù)并不是標(biāo)準(zhǔn)c/c++中規(guī)定的函數(shù),但是在許多編譯器中,廠商提供了其實(shí)現(xiàn)的版本。在gcc中,該函數(shù)名稱就snprintf,而在VC中稱為_snprintf。 如果你在VC中使用snprintf(),會(huì)提示此函數(shù)未聲明,改成_snprintf()即可。
注意點(diǎn):
1 sprintf是一個(gè)不安全函數(shù),src串的長度應(yīng)該小于dest緩沖區(qū)的大小,(如果src串的長度大于或等于dest緩沖區(qū)的大小,將會(huì)出現(xiàn)內(nèi)存溢出。)
2 snprintf中源串長度應(yīng)該小于目標(biāo)dest緩沖區(qū)的大小,且size等于目標(biāo)dest緩沖區(qū)的大小。(如果源串長度大于或等于目標(biāo)dest緩沖區(qū)的大小,且size等于目標(biāo)dest緩沖區(qū)的大小,則只會(huì)拷貝目標(biāo)dest緩沖區(qū)的大小減1個(gè)字符,后加’\0’;該情況下,如果size大于目標(biāo)dest緩沖區(qū)的大小則溢出。)
3 snprintf ()函數(shù)返回值問題, 如果輸出因?yàn)閟ize的限制而被截?cái)?#xff0c;返回值將是“如果有足夠空間存儲(chǔ),所應(yīng)能輸出的字符數(shù)(不包括字符串結(jié)尾的’\0’)”,這個(gè)值和size相等或者比size大!也就是說,如果可以寫入的字符串是”0123456789ABCDEF”共16位,但是size限制了是10,這樣 snprintf() 的返回值將會(huì)是16 而不是10!
四、snprintf和vsnprintf
同樣來看一個(gè)例子:
#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; }該程序可以編譯過,但是在運(yùn)行期間會(huì)崩潰,原因相信大家都能看的出來。那么,應(yīng)該如何處理呢?
#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函數(shù)有區(qū)別, 在linux代碼中,經(jīng)常見到snprintf(str, sizeof(str), “…”)這樣的用法, 為什么這里不是sizof(str) - 1呢?
我們看看Windows下這么用會(huì)怎樣:
#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; }我運(yùn)行的時(shí)候,程序沒有崩潰,算是萬幸。 但結(jié)果亂碼??磥?#xff0c;沒有自動(dòng)在str最后加’\0’, 在linux中, 就安全了, 會(huì)自動(dòng)補(bǔ)哈, 所以永遠(yuǎn)不會(huì)越界。
總結(jié)一下:
1. Linux中, 對于snprintf, 用sizeof(str), 最后會(huì)自動(dòng)加’\0’, 比strncpy更安全省事。
2. Windows中, 就把snprintf和strncpy理解為類似的, 要用sizeof(str) - 1, 需要注意最后的’\0’, 當(dāng)然啦,你可以在每次用strncpy之前,利用memset將串清零, 這樣比較好。VC++6.0中的_snprintf(snprintf)并沒有按要求實(shí)現(xiàn), 暈。
總結(jié)
以上是生活随笔為你收集整理的各种输出函数的比较(printf/fprintf/sprintf/snprintf/vprintf/vfprintf/vsprintf/vsnprintf)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 盖茨炮轰马斯克殖民火星雄心:这是在浪费钱
- 下一篇: 如何在keil下实现单工程多目标的设置(