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的聯絡與區別
- C語言與嵌入式C語言的區別C語言
- Java與C語言的區別?JavaC語言
- C語言 printf詳解C語言
- cookie與session的區別與聯絡CookieSession
- Session與Cookie的區別與聯絡SessionCookie
- JRE與JDK的區別與聯絡JDK
- C語言實現可變引數列表的system介面:巨集__VA_ARGS__C語言
- 跟你深入剖析可迭代物件和迭代器的區別與聯絡物件
- 可觀測性與傳統監控的區別和聯絡
- c裡面的printf, fprintf, sprintf, snprintf, printf_s, fprintf_s, sprintf_s, snprintf_s一問說清所有各種printf
- C語言--靜態區域性變數C語言變數
- C語言-變數常量資料型別C語言變數資料型別
- Kafka與ActiveMQ的區別與聯絡詳解KafkaMQ
- 詳解Kafka與ActiveMQ的區別與聯絡!KafkaMQ
- C/C++引用和指標的聯絡和區別C++指標
- c++可變模板引數C++
- c語言中陣列的宣告喝初始化的區別和聯絡C語言陣列
- javaSE中的==和equals的聯絡與區別Java
- 逍遙自在學C語言 | 變數、常量與資料型別C語言變數資料型別
- Css預編語言以及區別CSS
- HDFS 塊和 Input Splits 的區別與聯絡
- 程式和執行緒的區別與聯絡執行緒
- mongod命令的一些引數以及引數--pidfilepath與mongod.lock區別Go
- C語言——設計printf除錯巨集C語言除錯
- C++逆向 可變引數HookC++Hook
- C++11 可變引數模板C++
- 感知器、logistic與svm 區別與聯絡
- ipv4與ipv6的聯絡與區別
- jQuery與JavaScript與ajax三者的區別與聯絡jQueryJavaScript
- Java 的可變引數與 Collections 類Java
- 簡述Spring容器與SpringMVC的容器的聯絡與區別SpringMVC
- KPI vs OKR:區別與聯絡的終極指南KPIOKR
- Vue中watch、computed與methods的聯絡和區別Vue
- 單機、分散式、叢集的區別與聯絡分散式