vsprintf(string,fmt,ap);//將參數fmt、ap指向的可變參數一起轉換成格式化字符串,放string數組中,其作用同sprintf,只是參數類型不同
(1)格式化字符串
printf(“%s, %-19s: %6.2”,lastname,firstname,prize);
打印結果:Bechr,Teddy ;2000.00
我們說“ Bechr,Teddy ;2000.00 ”就是一個格式化字符串,printf的作用就是把(“%s, %-19s: 6.2”,lastname,firstname,prize)翻譯成電腦認識的字符串,而對於“%s, %-19s: 6.2”,lastname,firstname,prize電腦根部不認識,故需要printf翻譯。
(2)vsprintf
函數名: vsprintf
功 能: 送格式化輸出串到指定數組中
用 法: int vsprintf(char *string, char *format, va_list param);
程序例:
----------------------------------------------------------------
#include <stdio.h>
#include <conio.h>
#include <stdarg.h>
char buffer[80];
int vspf(char *fmt, ...)
{
va_list argptr;
int cnt;
va_start(argptr, fmt);
cnt = vsprintf(buffer, fmt, argptr);
va_end(argptr);
return(cnt);
}
int main(void)
{
int inumber = 30;
float fnumber = 90.0;
char string[4] = "abc";
vspf("%d %f %s", inumber, fnumber, string);
printf("%s\n", buffer);
return 0;
}
------------------------------------------------------------------------
vsprintf與sprintf功能是一樣的,即把格式化字符串輸出到指定數組中,sprintf(char *string, char *farmat [,argument,...])函數的參數從第二個參數開始與printf是一樣的,只是sprintf是輸出到指定數組中,printf是輸出到屏幕(一個標準輸出文件),因而sprintf多了char *string這參數。
(3)[轉]va_start va_arg va_end 的使用和原理
stdarg.h中查到如下宏定義:
typedef int *va_list[1];//va_list 變量類型定義
#define va_start(ap, parmN) (void)(*(ap) = __va_start(parmN))
#define va_arg(ap, type) __va_arg(*(ap), type)//取可變參數的內容
#define va_end(ap) ((void)(*(ap) = 0))
◎用法:
func( Type para1, Type para2, Type para3, ... )
{
/****** Step 1 ******/
va_list ap;
va_start( ap, para3 ); //一定要“...”之前的那個參數
/****** Step 2 ******/
//此時ap指向第一個可變參數
//調用va_arg取得裏面的值
Type xx = va_arg( ap, Type );
//Type一定要相同,如:
//char *p = va_arg( ap, char *);
//int i = va_arg( ap, int );
//如果有多個參數繼續調用va_arg,ap會自動變化(一般是自增)
/****** Step 3 ******/
va_end(ap); //For robust!
}
◎研究:(此處的宏定義與前面有些差別可能是OS不同,不用管它,只要能幫助我們理解即可)
typedef char * va_list;
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )//注意(ap += _INTSIZEOF(t))括號
#define _crt_va_end(ap) ( ap = (va_list)0 )
va_list argptr;
C語言的函數的參數是從右向左壓入堆棧的,調用va_start後,按定義的宏運算,_ADDRESSOF得到v所在的地址,然後這個地址加上v的大小,則使ap指向第一個可變參數如圖:
棧底 高地址
| .......
| 函數返回地址
| .......
| 函數最後一個參數
| ....
| 函數第一個可變參數 <--va_start後ap指向
| 函數最後一個固定參數
| 函數第一個固定參數
棧頂 低地址
然後,用va_arg()取得類型t的可變參數值, 先是讓ap指向下一個參數:
ap += _INTSIZEOF(t),然後在減去_INTSIZEOF(t),使得表達式結果爲
ap之前的值,即當前需要得到的參數的地址,強制轉換成指向此參數的
類型的指針,然後用*取值
最後,用va_end(ap),給ap初始化,保持健壯性。
小結:
可變參數的函數原理其實很簡單,而va系列是以宏定義來定義的,實現跟堆棧相關.我們寫一個可變函數的C函數時,有利也有弊,所以在不必要的場合,我們無需用到可變參數.如果在C++裏,我們應該利用C++的多態性來實現可變參數的功能,儘量避免用C語言的方式來實現.