C 语言中提供了一些基本字符串格式化处理函数,包括:
1)puts("this is a string"); 简单输出字符串到 standard output 中,该函数只有多字节版本;
2)int len = printf("1 + 2 = %d", 1 + 2); 格式化输出字符串到 standard output 中,返回值表示输出字符串长度,其 Unicode 版本为 wprintf(const wchar_t *_Format, ...);
3)int len = sprintf(szBuffer, "1 + 2 = %d", 1 + 2); 格式化输出字符串到 szBuffer 中,返回值表示输出字符串长度,其中 szBuffer 定义:char szBuffer[1024];
4)当需要自己构造 printf-like 格式函数,可以借助 vsprintf 函数,具体如下:
1 int MyMessageBox (char* szCaption, char* szFormat, ...) 2 { 3 char szBuffer [1024] ; 4 va_list pArgList ; 5 6 // The va_start macro (defined in STDARG.H) is usually equivalent to: 7 // pArgList = &szFormat + sizeof (szFormat) ; 8 9 va_start (pArgList, szFormat) ; 10 11 vsprintf (szBuffer, szFormat, pArgList) ; 12 13 // The va_end macro just zeroes out pArgList for no good reason 14 va_end (pArgList) ; 15 16 return MessageBoxA (NULL, szBuffer, szCaption, 0) ; 17 }
va_list 保存了函数参数栈第一个可变参数指针,结合格式化字符串 szFormat 给出每个格式化数据类型,vsprintf 访问每一个参数,完成格式化工作。这是可变参数函数工作原理。具体解析如下:
a. 定义 typedef char *va_list;
b. va_start (pArgList, szFormat) ;语句使 pArgList 指向第一个可变参数,szFormat 给出第一个可变参数前一个参数位置;
c. type va_arg(va_list ap, type);语句取出下一个可变参数值,其中 type 指定该可变参数类型;在以上代码中 vsprintf 内部完成了该工作;
d. va_end (pArgList) ; 语句关闭 pArgList 指针,保持程序健壮性;
以上方法实现可变参数访问,在函数调用中,函数参数从右到左入栈。只要直到其中任意一个参数位置,可以根据参数位置以及参数类型访问到任意一个参数。下面构造一个简单的可变参数访问函数以说明其原理:
1 // agrc 表示可变参数个数,可变参数类型均为整数 2 void ArgAnalyze(int argc, ...) 3 { 4 int *ptr = &argc; 5 ++ptr; 6 for(int i = 0; i < argc; ++i) 7 { 8 std::cout<<"argv"<<i + 1<<" = "<<*ptr<<std::endl; 9 ++ptr; 10 } 11 }
5) sprintf, vsprintf 函数存在一些缺陷,当传入 Buffer 空间小于格式化字符串空间时,函数会覆盖邻近区域,可能导致程序异常错误。因此,微软特别定义了一些安全函数:_snprintf, _vsnprintf,通过增加传入空间参数来避免该错误。同时,微软也给出了对应的 windows 版本函数,基本与C库版本一致。以下给出 sprintf, vsprintf 相关的安全版本, Unicode 版本以及通用版本:
ASCII Wide-Character Generic
standard version sprintf swprintf _stprintf
safe version _snprintf _snwprintf _sntprintf
windows version wsprintfA wsprintfW wsprintf
-------------------------------------------------------------------------------------
standar version vsprintf vswprintf _vstprintf
safe version _vsnprintf _vsnwprintf _vsntprintf
windows version wvsprintfA wvsprintfW wvsprintf
在 c++ 库中,也提供了相应的字符串格式化输出,具体如下:
1 // 输出到 standart output(多字节版本) 2 std::cout<<"1 + 2 = "<<1 + 2<<std::endl; 3 4 // 输出到 standart output(宽字节版本) 5 std::wcout<<L"1 + 2 = "<<1 + 2<<std::endl; 6 7 // 输出到字符串中(多字节版本) 8 std::stringstream ss; 9 ss<<"1 + 2 = "<<1 + 2<<std::endl; 10 std::string s = ss.str(); 11 12 // 输出到字符串中(宽字节版本) 13 std::wstringstream wss; 14 wss<<L"1 + 2 = "<<1 + 2<<std::endl; 15 std::wstring ws = wss.str();
参考资料 Programming Windows by Charles Petzold