1. 程式人生 > >[C/C++標準庫]_[初級]_[如何實現std::string自己的Format(sprintf)函式]

[C/C++標準庫]_[初級]_[如何實現std::string自己的Format(sprintf)函式]

場景:

1.  C語言有自己的sprintf函式,但是這個函式有個缺點,就是不知道需要建立多大的buffer, 這時候可以使用snprintf函式來計算大小,只要引數 buffer為NULL, count為0即可.

2.  這裡實現std::string自己的sprintf也是用了snprintf的特性,先計算大小,再建立空間,之後存入std::string.

3.  還使用了C的可變引數特性.

std::wstring Format(const wchar_t *format,...)
{
	va_list argptr;
	va_start(argptr, format);
	int count = _vsnwprintf(NULL,0,format,argptr);
	va_end(argptr);

	va_start(argptr, format);
	wchar_t* buf = (wchar_t*)malloc(count*sizeof(wchar_t));
	_vsnwprintf(buf,count,format,argptr);
	va_end(argptr);
	
	std::wstring str(buf,count);
	free(buf);
	return str;
}

讓我們看看可變引數的宣告:
typedef char *  va_list;

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#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)) )
#define _crt_va_end(ap)      ( ap = (va_list)0 )

注意: ap會累加,每次呼叫va_arg都會指向下一個引數,問題就是va_arg並不知道什麼時候結束,所以如果設計其他的可變引數的函式,要先傳入一個引數個數作為方法引數.

snprintf 原始碼實現是通過計算%的個數來判斷引數個數的.

參考:

If buffer is a null pointer and count is zero, len is returned as the count of characters required to format the output, not including the terminating null. 
To make a successful call with the same argument and locale parameters, allocate a buffer holding at least len + 1 characters.