C語言可變引數以及printf()、sprintf()、vsprintf() 的區別與聯絡
轉載自:https://blog.csdn.net/raito__/article/details/48860119
printf() 在控制檯應用程式中最為常用,使用也很簡單。其引數為格式化字串。
函式原型:printf(const char *format,[argument]);
例如:
int a=1,b=2;
printf("a=%d,b=%d",a,b);
輸出:
a=1,b=2
sprintf() 用於將輸出存到字元緩衝中。
函式原型:sprintf(char *buffer, const char *format, [argument]);
例如:
int a=1,b=2;
char s[10];
sprintf(s,"a=%d,b=%d",1,2);
puts(s);
輸出:
a=1,b=2
vsprintf() 與sprintf() 功能類似。
需要引入相關標頭檔案stdarg.h
函式原型: vsprintf(char *buffer, char *format, va_list param);
例如:
void Myprintf(const char* fmt,...);
int a=1,b=2;
char s[10];
Myprintf("a=%d,b=%d",a,b);
輸出:
a=1,b=2
因此可以用 vsprintf() 來實現 sprintf()。
其中 va_start、va_end、va_list 是什麼東東一會兒再解釋,先往下看。
既然sprintf() 與 vsprintf() 功能類似,為什麼不統一下,而且 vsprintf() 用法如此麻煩呢?
假設你想封裝一個子函式 Myprintf() ,其功能是將格式化字串輸出兩遍,若用 sprintf() 實現:
(錯誤的)程式碼:
void Myprintf(const char* fmt,...)
{
char s[10];
sprintf(s,fmt);
puts(s);
puts(s);
}
當你嘗試這麼呼叫該函式時:
int a=1,b=2;
Myprintf("a=%d,b=%d",a,b);
輸出:
a=?,b=?
a=?,b=?
我也不知道它會輸出幾,但一定不會是 a=1,b=2 。
這顯然是錯誤的!
為什麼呢?
當你這麼呼叫 Myprintf() 時,其中的sprintf() 實際上是這樣的:
sprintf(s,”a=%d,b=%d”)
而不是:
sprintf(s,”a=%d,b=%d”,a,b) !!
因為這麼呼叫的話只能解析到第一個字串而無法解析到後面的引數。
所以類似這種封裝用 sprintf() 是無法實現的,使用 sprintf() 只能原始的為它輸入所有的引數而不能以傳參的方式給它。
若用 vsprintf()那麼這個問題就可以迎刃而解。
函式如下:
void Myprintf(const char* fmt,...)
{
char s[10];
va_list ap;
va_start(ap,fmt);
vsprintf(s,fmt,ap);
va_end(ap);
puts(s);
puts(s);
}
當你這麼呼叫時:
Myprintf("a=%d,b=%d",a,b);
輸出:
a=1,b=2
a=1,b=2
成功了!哇哈哈哈哈!!想想也是有點小激動!!!
現在來解釋一下這個 vsprintf():
首先你要知道函式執行時,函式的引數是倒序壓入棧中的,vsprintf() 為了能夠解析你傳給它的多個引數,你必須告訴它引數從哪裡開始。
vadefs.h 標頭檔案中這麼定義 :typedef char * va_list,於是我們定義了一個 va_list ap 來儲存引數起始地址。
va_start(ap,fmt) 就找出這個函式在棧中排列的一堆引數的起始地址,然後直接瀏覽棧中引數,並用 vsprintf() 實現格式化字串的讀取,最後 vs_end(ap) 釋放ap,就像釋放指標一樣。通俗地說就是因為 vsprintf() 比 sprintf() 更加接近底層(棧),因此能實現這個目的,也是因此能用 vsprintf() 來實現 sprintf()。
打的手好酸~0.0 希望大家發現錯誤幫我指正,不幸感激!最後給懶得敲程式碼的童鞋附上這段演示程式碼:
#include <stdarg.h>
void Myprint(const char* fmt,...);
int main()
{
int a=1,b=2;
Myprintf("a=%d,b=%d",a,b);
return 0;
}
void Myprintf(const char* fmt,...)
{
char s[10];
va_list ap;
va_start(ap,fmt);
vsprintf(s,fmt,ap);
va_end(ap);
puts(s);
puts(s);
}
相關文章
- C語言可變引數詳解C語言
- C語言與C++有聯絡,有區別,這些內容要了解!C語言
- C語言怎麼實現可變引數C語言
- B/S與C/S的聯絡與區別
- CreateThread()、_beginthread()以及_beginthreadex()聯絡與區別thread
- Swift語言中class、struct、enum的聯絡與區別SwiftStruct
- C語言與嵌入式C語言的區別C語言
- Java與C語言的區別?JavaC語言
- cookie與session的區別與聯絡CookieSession
- Session與Cookie的區別與聯絡SessionCookie
- 同步與阻塞的區別與聯絡
- hive與hbase的聯絡與區別Hive
- onchange與onpropertychange的聯絡與區別!
- BS與CS的聯絡與區別
- define與typedef區別與聯絡
- C語言 printf詳解C語言
- JRE與JDK的區別與聯絡JDK
- C語言實現可變引數列表的system介面:巨集__VA_ARGS__C語言
- Session和Cookie的聯絡與區別SessionCookie
- CGI與Servlet的區別和聯絡Servlet
- Session和Cookie的區別與聯絡SessionCookie
- 跟你深入剖析可迭代物件和迭代器的區別與聯絡物件
- 可觀測性與傳統監控的區別和聯絡
- Apache與Tomcat 區別聯絡ApacheTomcat
- Kafka與ActiveMQ的區別與聯絡詳解KafkaMQ
- 詳解Kafka與ActiveMQ的區別與聯絡!KafkaMQ
- 關於HashSet與TreeSet的區別與聯絡
- Oracle FailSafe與rac的聯絡與區別OracleAI
- Android與Java ME的區別與聯絡AndroidJava
- COM與DCOM的區別與聯絡 (轉)
- c裡面的printf, fprintf, sprintf, snprintf, printf_s, fprintf_s, sprintf_s, snprintf_s一問說清所有各種printf
- c# get_type與typeof方法的區別與聯絡和作用C#
- C語言--靜態區域性變數C語言變數
- Linux和Ubuntu的區別與聯絡LinuxUbuntu
- connection session process的聯絡與區別Session
- COM/DCOM的區別與聯絡 (轉)
- C可變引數的實現
- C語言-變數常量資料型別C語言變數資料型別