C語言可變引數以及printf()、sprintf()、vsprintf() 的區別與聯絡

Sun_Shine_999發表於2018-06-12

轉載自: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);  
}  

相關文章